From: Akihiro Sagawa Subject: [10/11] kernel32: Implement LCMAP_FULLWIDTH. Message-Id: <20160923000301.B243.375B48EC@gmail.com> Date: Fri, 23 Sep 2016 00:06:20 +0900 Signed-off-by: Akihiro Sagawa --- dlls/kernel32/locale.c | 160 ++++++++++++++++++++++++++++++++++++++++++- dlls/kernel32/tests/locale.c | 12 ++-- 2 files changed, 167 insertions(+), 5 deletions(-) diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c index 842922a..5ef1990 100644 --- a/dlls/kernel32/locale.c +++ b/dlls/kernel32/locale.c @@ -3111,6 +3111,133 @@ BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LP return GetStringTypeA(locale, type, src, count, chartype); } +/* compose a fullwidth katakana. return consumed source characters. */ +static INT compose_katakana( LPCWSTR src, INT srclen, LPWSTR dst ) +{ + const static BYTE katakana_map[] = { + /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */ + 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */ + 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */ + 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */ + 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */ + 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */ + 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */ + 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */ + }; + WCHAR before, dummy; + + if (!dst) + dst = &dummy; + + switch (*src) + { + case 0x309b: case 0x309c: + *dst = *src - 2; + return 1; + case 0x30f0: case 0x30f1: case 0x30fd: + *dst = *src; + break; + default: + { + int shift = *src - 0xff61; + if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]) ) + return 0; + else + *dst = katakana_map[shift] | 0x3000; + } + } + + if (srclen <= 1) + return 1; + + before = *dst; + + /* datakuten (voiced sound) */ + if (*(src + 1) == 0xff9e) + { + if ((*src >= 0xff76 && *src <= 0xff84) || + (*src >= 0xff8a && *src <= 0xff8e) || + *src == 0x30fd) + *dst += 1; + else if (*src == 0xff73) + *dst = 0x30f4; /* KATAKANA LETTER VU */ + else if (*src == 0xff9c) + *dst = 0x30f7; /* KATAKANA LETTER VA */ + else if (*src == 0x30f0) + *dst = 0x30f8; /* KATAKANA LETTER VI */ + else if (*src == 0x30f1) + *dst = 0x30f9; /* KATAKANA LETTER VE */ + else if (*src == 0xff66) + *dst = 0x30fa; /* KATAKANA LETTER VO */ + } + + /* handakuten (semi-voiced sound) */ + if (*(src + 1) == 0xff9f) + if (*src >= 0xff8a && *src <= 0xff8e) + *dst += 2; + + return (*dst != before) ? 2 : 1; +} + +/* map half-width (narrow) character to full-width (wide) one */ +static INT map_to_fullwidth( LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen ) +{ + LPWSTR dst_ptr; + for (dst_ptr = dst; srclen && dstlen; src++, srclen--, dst_ptr++, dstlen--) + { + INT n; + if (*src <= '~' && *src > ' ' && *src != '\\') + *dst_ptr = *src - 0x20 + 0xff00; + else if (*src == ' ') + *dst_ptr = 0x3000; + else if (*src <= 0x00af && *src >= 0x00a2) + { + const static WCHAR misc_symbols_map[] = { + 0xffe0, 0xffe1, 0x00a4, 0xffe5, 0xffe4, 0x00a7, 0x00a8, + 0x00a9, 0x00aa, 0x00ab, 0xffe2, 0x00ad, 0x00ae, 0xffe3 + }; + *dst_ptr = misc_symbols_map[*src - 0x00a2]; + } + else if (*src == 0x20a9) /* WON SIGN */ + *dst_ptr = 0xffe6; + else if ((n = compose_katakana(src, srclen, dst_ptr)) > 0) + { + if (n == 2) + { + src++; + srclen--; + } + } + else if (*src >= 0xffa0 && *src <= 0xffdc) + { + const static signed char hangul_shift_table[] = { + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* U+FFA0- */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, /* U+FFB0- */ + -1, -1, 3, 3, 3, 3, 3, 3, -1, -1, 5, 5, 5, 5, 5, 5, /* U+FFC0- */ + -1, -1, 7, 7, 7, 7, 7, 7, -1, -1, 9, 9, 9 /* U+FFD0- */ + }; + int hangul_shift = hangul_shift_table[*src - 0xffa0]; + + if (hangul_shift < 0) + *dst_ptr = *src; + else if (hangul_shift < 16) + *dst_ptr = (*src - 0xffa0) - hangul_shift + 0x3130; + else + *dst_ptr = 0x3164; + } + else + *dst_ptr = *src; + } + + if (srclen) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + + return dst_ptr - dst; +} + /************************************************************************* * LCMapStringEx (KERNEL32.@) * @@ -3134,6 +3261,7 @@ BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LP INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam) { + LPWSTR buffer = NULL; LPWSTR dst_ptr; INT len; @@ -3194,7 +3322,7 @@ INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPW } if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) && (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) || - ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) && + ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_FULLWIDTH)) && (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE)))) { SetLastError(ERROR_INVALID_FLAGS); @@ -3222,6 +3350,17 @@ INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPW len++; } } + else if (flags & LCMAP_FULLWIDTH) + { + for (len = 0; srclen; src++, srclen--, len++) + { + if (compose_katakana(src, srclen, NULL) >= 2) + { + src++; + srclen--; + } + } + } else len = srclen; return len; @@ -3246,6 +3385,24 @@ INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPW goto done; } + if (flags & LCMAP_FULLWIDTH) + { + len = min(srclen, dstlen); + buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!buffer) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + srclen = map_to_fullwidth(src, srclen, buffer, len); + if (!srclen) + { + HeapFree(GetProcessHeap(), 0, buffer); + return 0; + } + src = buffer; + } + if (flags & LCMAP_UPPERCASE) { for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--) @@ -3294,6 +3451,7 @@ INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPW } done: + HeapFree(GetProcessHeap(), 0, buffer); if (srclen) { SetLastError(ERROR_INSUFFICIENT_BUFFER); diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 5721979..0f55de0 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -2214,6 +2214,10 @@ static const DWORD lcmap_invalid_flags[] = { LCMAP_KATAKANA | NORM_IGNORESYMBOLS, LCMAP_KATAKANA | LCMAP_SIMPLIFIED_CHINESE, LCMAP_KATAKANA | LCMAP_TRADITIONAL_CHINESE, + LCMAP_FULLWIDTH | NORM_IGNORENONSPACE, + LCMAP_FULLWIDTH | NORM_IGNORESYMBOLS, + LCMAP_FULLWIDTH | LCMAP_SIMPLIFIED_CHINESE, + LCMAP_FULLWIDTH | LCMAP_TRADITIONAL_CHINESE, }; static void test_LCMapStringA(void) @@ -2461,9 +2465,9 @@ static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *f /* test LCMAP_FULLWIDTH */ ret = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, buf, sizeof(buf)/sizeof(WCHAR)); - todo_wine ok(ret == lstrlenW(japanese_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name, + ok(ret == lstrlenW(japanese_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name, ret, GetLastError(), lstrlenW(japanese_text) + 1); - todo_wine ok(!lstrcmpW(buf, japanese_text), "%s string compare mismatch\n", func_name); + ok(!lstrcmpW(buf, japanese_text), "%s string compare mismatch\n", func_name); ret2 = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, NULL, 0); ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret2, ret); @@ -2472,9 +2476,9 @@ static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *f (half-width katakana is converted into full-wdith hiragana) */ ret = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, buf, sizeof(buf)/sizeof(WCHAR)); - todo_wine ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name, + ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name, ret, GetLastError(), lstrlenW(hiragana_text) + 1); - todo_wine ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name); + ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name); ret2 = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, NULL, 0); ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);