[A mod for Fallout: New Vegas relies on rewind not modifying the first argument passed to it.](https://github.com/jazzisparis/UIO-User-Interface-Organizer/blob/426d96391f9...) Since it only links to api-ms-win-crt-stdio I've kept the thunk usage to just ucrtbase.
-- v3: msvcrt, ucrtbase: Introduce rewind_preserve_stack.
From: Victor Chiletto v@hnn.net.br
This is a small thunk to rewind that preserves the first argument passed. This is needed because on some versions of GCC, rewind's call to _unlock_file is tail-call optimized, which modifies the stack. --- dlls/msvcrt/file.c | 17 + dlls/ucrtbase/tests/misc.c | 48 ++ dlls/ucrtbase/ucrtbase.spec | 3 +- meme.patch | 22 + patch.diff | 1455 +++++++++++++++++++++++++++++++++++ 5 files changed, 1544 insertions(+), 1 deletion(-) create mode 100644 meme.patch create mode 100644 patch.diff
diff --git a/dlls/msvcrt/file.c b/dlls/msvcrt/file.c index 58a607e3bbe..b52c88a7fac 100644 --- a/dlls/msvcrt/file.c +++ b/dlls/msvcrt/file.c @@ -45,6 +45,10 @@
#include "wine/debug.h"
+#if defined (__i386__) +#include "wine/asm.h" +#endif + WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
#undef _fstat @@ -1658,6 +1662,19 @@ int CDECL clearerr_s(FILE* file) return 0; }
+#if defined(__i386__) +/* Stack preserving thunk for rewind + * needed for the UIO mod for Fallout: New Vegas + */ +__ASM_GLOBAL_FUNC(rewind_preserve_stack, + "pushl 4(%esp)\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "call "__ASM_NAME("rewind") "\n\t" + "popl %eax\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") + "ret") +#endif + /********************************************************************* * rewind (MSVCRT.@) */ diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c index 069de0787b7..0ad1804d0d6 100644 --- a/dlls/ucrtbase/tests/misc.c +++ b/dlls/ucrtbase/tests/misc.c @@ -38,6 +38,10 @@ #include <winbase.h> #include "wine/test.h"
+#if defined(__i386__) && defined(__GNUC__) +#include "wine/asm.h" +#endif + #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
@@ -1591,6 +1595,47 @@ static void test_fopen_exclusive( void ) unlink(path); }
+#if defined(__i386__) +#include "pshpack1.h" +struct rewind_thunk { + BYTE push_esp[4]; /* push [esp+0x4] */ + BYTE call_rewind; /* call */ + DWORD rewind_addr; /* relative addr of rewind */ + BYTE pop_eax; /* pop eax */ + BYTE ret; /* ret */ +}; +#include "poppack.h" + +static FILE * (CDECL *test_rewind_wrapper)(FILE *fp); + +static void test_rewind_i386_abi(void) +{ + FILE *fp_in, *fp_out; + + struct rewind_thunk *thunk = VirtualAlloc(NULL, sizeof(*thunk), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + thunk->push_esp[0] = 0xff; + thunk->push_esp[1] = 0x74; + thunk->push_esp[2] = 0x24; + thunk->push_esp[3] = 0x04; + + thunk->call_rewind = 0xe8; + thunk->rewind_addr = (BYTE *) rewind - (BYTE *) (&thunk->rewind_addr + 1); + + thunk->pop_eax = 0x58; + thunk->ret = 0xc3; + + test_rewind_wrapper = (void *) thunk; + + fp_in = fopen("rewind_abi.tst", "wb"); + fp_out = test_rewind_wrapper(fp_in); + ok(fp_in == fp_out, "rewind modified the first argument in the stack\n"); + + fclose(fp_in); + unlink("rewind_abi.tst"); +} +#endif + START_TEST(misc) { int arg_c; @@ -1633,4 +1678,7 @@ START_TEST(misc) test_thread_storage(); test_fenv(); test_fopen_exclusive(); +#if defined(__i386__) + test_rewind_i386_abi(); +#endif } diff --git a/dlls/ucrtbase/ucrtbase.spec b/dlls/ucrtbase/ucrtbase.spec index bb505b42afb..ff4d8845f1e 100644 --- a/dlls/ucrtbase/ucrtbase.spec +++ b/dlls/ucrtbase/ucrtbase.spec @@ -2470,7 +2470,8 @@ @ cdecl remquof(float float ptr) @ cdecl remquol(double double ptr) remquo @ cdecl rename(str str) -@ cdecl rewind(ptr) +@ cdecl -arch=i386 rewind(ptr) rewind_preserve_stack +@ cdecl -arch=!i386 rewind(ptr) rewind @ cdecl rint(double) MSVCRT_rint @ cdecl rintf(float) @ cdecl rintl(double) MSVCRT_rint diff --git a/meme.patch b/meme.patch new file mode 100644 index 00000000000..170f73be9ed --- /dev/null +++ b/meme.patch @@ -0,0 +1,22 @@ +diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c +index 05196b22d1b..252139d52b5 100644 +--- a/dlls/msvcrt/locale.c ++++ b/dlls/msvcrt/locale.c +@@ -1373,7 +1373,7 @@ static pthreadlocinfo create_locinfo(int category, + #if _MSVCR_VER >= 110 + if(old_locinfo) { + locale_sname[i] = wcsdup(old_locinfo->lc_name[i]); +- if (!locale_sname[i]) ++ if (old_locinfo->lc_name[i] && !locale_sname[i]) + goto fail; + } + #else +@@ -1398,7 +1398,7 @@ static pthreadlocinfo create_locinfo(int category, + if(old_locinfo) { + #if _MSVCR_VER >= 110 + locale_sname[i] = wcsdup(old_locinfo->lc_name[i]); +- if(!locale_sname[i]) ++ if(old_locinfo->lc_name[i] && !locale_sname[i]) + goto fail; + #else + int sname_size; diff --git a/patch.diff b/patch.diff new file mode 100644 index 00000000000..6b1254ffe4f --- /dev/null +++ b/patch.diff @@ -0,0 +1,1455 @@ +diff --git a/dlls/msvcr110/tests/msvcr110.c b/dlls/msvcr110/tests/msvcr110.c +index 255c68b0796..121b8063147 100644 +--- a/dlls/msvcr110/tests/msvcr110.c ++++ b/dlls/msvcr110/tests/msvcr110.c +@@ -152,6 +152,84 @@ static void test_setlocale(void) + ret = p_setlocale(LC_ALL, "en-us.1250"); + ok(!ret, "setlocale(en-us.1250) succeeded (%s)\n", ret); + ++ ret = p_setlocale(LC_ALL, "zh-Hans"); ++ ok((ret != NULL ++ || broken(ret == NULL)), /* Vista */ ++ "expected success, but got NULL\n"); ++ if (ret) ++ ok(!strcmp(ret, "zh-Hans"), "setlocale zh-Hans failed\n"); ++ ++ ret = p_setlocale(LC_ALL, "zh-Hant"); ++ ok((ret != NULL ++ || broken(ret == NULL)), /* Vista */ ++ "expected success, but got NULL\n"); ++ if (ret) ++ ok(!strcmp(ret, "zh-Hant"), "setlocale zh-Hant failed\n"); ++ ++ /* used to return Chinese (Simplified)_China.936 */ ++ ret = p_setlocale(LC_ALL, "chinese"); ++ ok(ret != NULL, "expected success, but got NULL\n"); ++ if (ret) ++ ok((!strcmp(ret, "Chinese_China.936") ++ || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936")) /* Vista */ ++ || broken(!strcmp(ret, "Chinese_People's Republic of China.936"))), /* 7 */ ++ "setlocale chinese failed, got %s\n", ret); ++ ++ /* used to return Chinese (Simplified)_China.936 */ ++ ret = p_setlocale(LC_ALL, "Chinese_China.936"); ++ ok(ret != NULL, "expected success, but got NULL\n"); ++ if (ret) ++ ok((!strcmp(ret, "Chinese_China.936") ++ || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936")) /* Vista */ ++ || broken(!strcmp(ret, "Chinese_People's Republic of China.936"))), /* 7 */ ++ "setlocale Chinese_China.936 failed, got %s\n", ret); ++ ++ /* used to return Chinese (Simplified)_China.936 */ ++ ret = p_setlocale(LC_ALL, "chinese-simplified"); ++ ok(ret != NULL, "expected success, but got NULL\n"); ++ if (ret) ++ ok((!strcmp(ret, "Chinese_China.936") ++ || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936"))), /* Vista */ ++ "setlocale chinese-simplified failed, got %s\n", ret); ++ ++ /* used to return Chinese (Simplified)_China.936 */ ++ ret = p_setlocale(LC_ALL, "chs"); ++ ok(ret != NULL, "expected success, but got NULL\n"); ++ if (ret) ++ ok((!strcmp(ret, "Chinese_China.936") ++ || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936"))), /* Vista */ ++ "setlocale chs failed, got %s\n", ret); ++ ++ /* used to return Chinese (Traditional)_Taiwan.950 */ ++ ret = p_setlocale(LC_ALL, "cht"); ++ ok(ret != NULL, "expected success, but got NULL\n"); ++ if (ret) ++ ok((!strcmp(ret, "Chinese (Traditional)_Hong Kong SAR.950") ++ || broken(!strcmp(ret, "Chinese (Traditional)_Taiwan.950"))), /* Vista - 7 */ ++ "setlocale cht failed, got %s\n", ret); ++ ++ /* used to return Chinese (Traditional)_Taiwan.950 */ ++ ret = p_setlocale(LC_ALL, "chinese-traditional"); ++ ok(ret != NULL, "expected success, but got NULL\n"); ++ if (ret) ++ ok((!strcmp(ret, "Chinese (Traditional)_Hong Kong SAR.950") ++ || broken(!strcmp(ret, "Chinese (Traditional)_Taiwan.950"))), /* Vista - 7 */ ++ "setlocale chinese-traditional failed, got %s\n", ret); ++ ++ ret = p_setlocale(LC_ALL, "norwegian-nynorsk"); ++ ok(ret != NULL, "expected success, but got NULL\n"); ++ if (ret) ++ ok((!strcmp(ret, "Norwegian Nynorsk_Norway.1252") ++ || broken(!strcmp(ret, "Norwegian (Nynorsk)_Norway.1252"))), /* Vista - 7 */ ++ "setlocale norwegian-nynorsk failed, got %s\n", ret); ++ ++ ret = p_setlocale(LC_ALL, "non"); ++ ok(ret != NULL, "expected success, but got NULL\n"); ++ if (ret) ++ ok((!strcmp(ret, "Norwegian Nynorsk_Norway.1252") ++ || broken(!strcmp(ret, "Norwegian (Nynorsk)_Norway.1252"))), /* Vista - 7 */ ++ "setlocale norwegian-nynorsk failed, got %s\n", ret); ++ + p_setlocale(LC_ALL, "C"); + } + +diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c +index e5a5ff0435d..838338c83f8 100644 +--- a/dlls/msvcr120/tests/msvcr120.c ++++ b/dlls/msvcr120/tests/msvcr120.c +@@ -596,6 +596,14 @@ static void test____lc_locale_name_func(void) + } + } + ++ p_setlocale(LC_ALL, "zh-Hans"); ++ lc_names = p____lc_locale_name_func(); ++ ok(!lstrcmpW(lc_names[1], L"zh-Hans"), "lc_names[1] expected zh-Hans got %s\n", wine_dbgstr_w(lc_names[1])); ++ ++ p_setlocale(LC_ALL, "zh-Hant"); ++ lc_names = p____lc_locale_name_func(); ++ ok(!lstrcmpW(lc_names[1], L"zh-Hant"), "lc_names[1] expected zh-Hant got %s\n", wine_dbgstr_w(lc_names[1])); ++ + p_setlocale(LC_ALL, "C"); + lc_names = p____lc_locale_name_func(); + ok(!lc_names[1], "___lc_locale_name_func()[1] = %s\n", wine_dbgstr_w(lc_names[1])); +diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c +index 16ed68cdebd..c14c0b88331 100644 +--- a/dlls/msvcrt/locale.c ++++ b/dlls/msvcrt/locale.c +@@ -166,43 +166,54 @@ static struct lconv cloc_lconv = + /* Friendly country strings & language names abbreviations. */ + static const char * const _country_synonyms[] = + { +- "american", "enu", +- "american english", "enu", +- "american-english", "enu", +- "english-american", "enu", +- "english-us", "enu", +- "english-usa", "enu", +- "us", "enu", +- "usa", "enu", +- "australian", "ena", +- "english-aus", "ena", +- "belgian", "nlb", +- "french-belgian", "frb", +- "canadian", "enc", +- "english-can", "enc", +- "french-canadian", "frc", +- "chinese", "chs", +- "chinese-simplified", "chs", +- "chinese-traditional", "cht", +- "dutch-belgian", "nlb", +- "english-nz", "enz", +- "uk", "eng", +- "english-uk", "eng", +- "french-swiss", "frs", +- "swiss", "des", +- "german-swiss", "des", +- "italian-swiss", "its", +- "german-austrian", "dea", +- "portuguese", "ptb", +- "portuguese-brazil", "ptb", +- "spanish-mexican", "esm", +- "norwegian-bokmal", "nor", +- "norwegian-nynorsk", "non", +- "spanish-modern", "esn" ++ "american", "en", ++ "american english", "en-US", ++ "american-english", "en-US", ++ "english-american", "en-US", ++ "english-us", "en-US", ++ "english-usa", "en-US", ++ "us", "en-US", ++ "usa", "en-US", ++ "australian", "en-AU", ++ "english-aus", "en-AU", ++ "belgian", "nl-BE", ++ "french-belgian", "fr-BE", ++ "canadian", "en-CA", ++ "english-can", "en-CA", ++ "french-canadian", "fr-CA", ++#if _MSVCR_VER >= 110 ++ "chinese", "zh", ++ "chinese-simplified", "zh", ++ "chinese-traditional", "zh-HK", ++ "chs", "zh", ++ "cht", "zh-HK", ++#else ++ "chinese", "zh-CN", ++ "chinese-simplified", "zh-CN", ++ "chinese-traditional", "zh-TW", ++ "chs", "zh-CN", ++ "cht", "zh-TW", ++#endif ++ "dutch-belgian", "nl-BE", ++ "english-nz", "en-NZ", ++ "uk", "en-GB", ++ "english-uk", "en-GB", ++ "french-swiss", "fr-CH", ++ "swiss", "de-CH", ++ "german-swiss", "de-CH", ++ "italian-swiss", "it-CH", ++ "german-austrian", "de-AT", ++ "portuguese", "pt-BR", ++ "portuguese-brazil", "pt-BR", ++ "spanish-mexican", "es-MX", ++ "norwegian-bokmal", "nb", ++ "norwegian-nynorsk", "nn-NO", ++ "spanish-modern", "es-ES" + }; + ++ + /* INTERNAL: Map a synonym to an ISO code */ +-static void remap_synonym(char *name) ++static BOOL remap_synonym(char *name) + { + unsigned int i; + for (i = 0; i < ARRAY_SIZE(_country_synonyms); i += 2) +@@ -211,9 +222,11 @@ static void remap_synonym(char *name) + { + TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]); + strcpy(name, _country_synonyms[i+1]); +- return; ++ return TRUE; + } + } ++ ++ return FALSE; + } + + /* Note: Flags are weighted in order of matching importance */ +@@ -222,11 +235,11 @@ static void remap_synonym(char *name) + #define FOUND_COUNTRY 0x1 + + typedef struct { +- char search_language[MAX_ELEM_LEN]; +- char search_country[MAX_ELEM_LEN]; ++ WCHAR search_language[MAX_ELEM_LEN]; ++ WCHAR search_country[MAX_ELEM_LEN]; ++ WCHAR found_lang_sname[LOCALE_NAME_MAX_LENGTH]; + DWORD found_codepage; + unsigned int match_flags; +- LANGID found_lang_id; + BOOL allow_sname; + } locale_search_t; + +@@ -234,52 +247,48 @@ typedef struct { + #define STOP_LOOKING FALSE + + /* INTERNAL: Get and compare locale info with a given string */ +-static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp, BOOL exact) ++static int compare_info(WCHAR *name, DWORD flags, WCHAR *buff, const WCHAR *cmp, BOOL exact) + { + int len; + + if(!cmp[0]) +- return 0; ++ return 0; + + buff[0] = 0; +- GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN); ++ GetLocaleInfoEx(name, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN); + if (!buff[0]) + return 0; + + /* Partial matches are only allowed on language/country names */ +- len = strlen(cmp); ++ len = wcslen(cmp); ++ + if(exact || len<=3) +- return !_stricmp(cmp, buff); ++ return !_wcsicmp(cmp, buff); + else +- return !_strnicmp(cmp, buff, len); ++ return !_wcsnicmp(cmp, buff, len); + } + + static BOOL CALLBACK + find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) + { + locale_search_t *res = (locale_search_t *)lParam; +- const LCID lcid = LocaleNameToLCID( name, 0 ); +- char buff[MAX_ELEM_LEN]; ++ WCHAR buff[MAX_ELEM_LEN]; + unsigned int flags = 0; + +- if (lcid == LOCALE_CUSTOM_UNSPECIFIED) return CONTINUE_LOOKING; +- +-#if _MSVCR_VER >= 110 +- if (res->allow_sname && compare_info(lcid,LOCALE_SNAME,buff,res->search_language, TRUE)) ++ if (res->allow_sname && compare_info(name,LOCALE_SNAME,buff,res->search_language, TRUE)) + { +- TRACE(":Found locale: %s->%s\n", res->search_language, buff); ++ TRACE(":Found locale: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff)); + res->match_flags = FOUND_SNAME; +- res->found_lang_id = LANGIDFROMLCID(lcid); ++ wcscpy(res->found_lang_sname, name); + return STOP_LOOKING; + } +-#endif + + /* Check Language */ +- if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) || +- compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) || +- compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE)) ++ if (compare_info(name,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) || ++ compare_info(name,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) || ++ compare_info(name,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE)) + { +- TRACE(":Found language: %s->%s\n", res->search_language, buff); ++ TRACE(":Found language: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff)); + flags |= FOUND_LANGUAGE; + } + else if (res->match_flags & FOUND_LANGUAGE) +@@ -288,11 +297,11 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) + } + + /* Check Country */ +- if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) || +- compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) || +- compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE)) ++ if (compare_info(name,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) || ++ compare_info(name,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) || ++ compare_info(name,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE)) + { +- TRACE("Found country:%s->%s\n", res->search_country, buff); ++ TRACE("Found country:%s->%s\n", wine_dbgstr_w(res->search_country), wine_dbgstr_w(buff)); + flags |= FOUND_COUNTRY; + } + else if (!flags && (res->match_flags & FOUND_COUNTRY)) +@@ -304,7 +313,7 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) + { + /* Found a better match than previously */ + res->match_flags = flags; +- res->found_lang_id = LANGIDFROMLCID(lcid); ++ wcscpy(res->found_lang_sname, name); + } + if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY)) == + (FOUND_LANGUAGE | FOUND_COUNTRY)) +@@ -315,76 +324,94 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) + return CONTINUE_LOOKING; + } + +-/* Internal: Find the LCID for a locale specification */ +-LCID locale_to_LCID(const char *locale, unsigned short *codepage, BOOL *sname) ++/* Internal: Find the sname for a locale specification */ ++BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_match, WCHAR *sname, int sname_size) + { + thread_data_t *data = msvcrt_get_thread_data(); + const char *cp, *region; + BOOL is_sname = FALSE; + DWORD locale_cp; +- LCID lcid; + + if (!strcmp(locale, data->cached_locale)) { + if (codepage) + *codepage = data->cached_cp; + if (sname) +- *sname = data->cached_sname; +- return data->cached_lcid; ++ wcsncpy(sname, data->cached_sname, sname_size); ++ return TRUE; + } + + cp = strchr(locale, '.'); + region = strchr(locale, '_'); + + if(!locale[0] || (cp == locale && !region)) { +- lcid = GetUserDefaultLCID(); ++ GetUserDefaultLocaleName(sname, sname_size); + } else { ++ char search_language_buf[MAX_ELEM_LEN] = { 0 }, search_country_buf[MAX_ELEM_LEN] = { 0 }; + locale_search_t search; ++ BOOL remapped = FALSE; + + memset(&search, 0, sizeof(locale_search_t)); +- lstrcpynA(search.search_language, locale, MAX_ELEM_LEN); ++ lstrcpynA(search_language_buf, locale, MAX_ELEM_LEN); + if(region) { +- lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN); ++ lstrcpynA(search_country_buf, region+1, MAX_ELEM_LEN); + if(region-locale < MAX_ELEM_LEN) +- search.search_language[region-locale] = '\0'; ++ search_language_buf[region-locale] = '\0'; + } else +- search.search_country[0] = '\0'; ++ search_country_buf[0] = '\0'; + + if(cp) { + if(region && cp-region-1<MAX_ELEM_LEN) +- search.search_country[cp-region-1] = '\0'; ++ search_country_buf[cp-region-1] = '\0'; + if(cp-locale < MAX_ELEM_LEN) +- search.search_language[cp-locale] = '\0'; ++ search_language_buf[cp-locale] = '\0'; ++ } ++ ++ if ((remapped = remap_synonym(search_language_buf))) ++ { ++ search.allow_sname = TRUE; + } + ++#if _MSVCR_VER >= 110 + if(!cp && !region) + { +- remap_synonym(search.search_language); + search.allow_sname = TRUE; + } ++#endif ++ ++ MultiByteToWideChar(CP_ACP, 0, search_language_buf, -1, search.search_language, MAX_ELEM_LEN); ++ if (search.allow_sname && IsValidLocaleName(search.search_language)) ++ { ++ search.match_flags = FOUND_SNAME; ++ wcsncpy(sname, search.search_language, sname_size); ++ } ++ else ++ { ++ MultiByteToWideChar(CP_ACP, 0, search_country_buf, -1, search.search_country, MAX_ELEM_LEN); ++ EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL); + +- EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL); ++ if (!search.match_flags) ++ return FALSE; + +- if (!search.match_flags) +- return -1; ++ /* If we were given something that didn't match, fail */ ++ if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE))) ++ return FALSE; ++ if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY)) ++ return FALSE; + +- /* If we were given something that didn't match, fail */ +- if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE))) +- return -1; +- if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY)) +- return -1; ++ wcsncpy(sname, search.found_lang_sname, sname_size); ++ } + +- lcid = MAKELCID(search.found_lang_id, SORT_DEFAULT); +- is_sname = (search.match_flags & FOUND_SNAME) != 0; ++ is_sname = !remapped && (search.match_flags & FOUND_SNAME) != 0; + } + + /* Obtain code page */ + if (!cp || !cp[1] || !_strnicmp(cp, ".ACP", 4)) { +- GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, ++ GetLocaleInfoEx(sname, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR)); + if (!locale_cp) + locale_cp = GetACP(); + } else if (!_strnicmp(cp, ".OCP", 4)) { +- GetLocaleInfoW(lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER, ++ GetLocaleInfoEx(sname, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER, + (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR)); + #if _MSVCR_VER >= 140 + } else if (!_strnicmp(cp, ".UTF-8", 6) +@@ -395,24 +422,23 @@ LCID locale_to_LCID(const char *locale, unsigned short *codepage, BOOL *sname) + locale_cp = atoi(cp + 1); + } + if (!IsValidCodePage(locale_cp)) +- return -1; ++ return FALSE; + + if (!locale_cp) +- return -1; ++ return FALSE; + + if (codepage) + *codepage = locale_cp; +- if (sname) +- *sname = is_sname; ++ if (sname_match) ++ *sname_match = is_sname; + + if (strlen(locale) < sizeof(data->cached_locale)) { + strcpy(data->cached_locale, locale); +- data->cached_lcid = lcid; + data->cached_cp = locale_cp; +- data->cached_sname = is_sname; ++ wcscpy(data->cached_sname, sname); + } + +- return lcid; ++ return TRUE; + } + + static void copy_threadlocinfo_category(pthreadlocinfo locinfo, +@@ -469,8 +495,8 @@ static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat) + |LOCALE_NOUSEROVERRIDE, buf, 100); + if(!len) return FALSE; + +- if(LocaleNameToLCID(buf, 0) != lcid) +- len = LCIDToLocaleName(lcid, buf, 100, 0); ++ if(LocaleNameToLCID(buf, LOCALE_ALLOW_NEUTRAL_NAMES) != lcid) ++ len = LCIDToLocaleName(lcid, buf, 100, LOCALE_ALLOW_NEUTRAL_NAMES); + + if(!len || !(locinfo->lc_name[cat] = malloc(len*sizeof(wchar_t)))) + return FALSE; +@@ -486,13 +512,13 @@ static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat) + #endif + + /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */ +-static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp, ++static BOOL update_threadlocinfo_category(WCHAR *sname, unsigned short cp, + pthreadlocinfo locinfo, int category) + { +- char buf[256], *p; ++ WCHAR wbuf[256], *p; + +- if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) { +- p = buf; ++ if(GetLocaleInfoEx(sname, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, 256)) { ++ p = wbuf; + + locinfo->lc_id[category].wLanguage = 0; + while(*p) { +@@ -512,26 +538,32 @@ static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp, + + locinfo->lc_id[category].wCodePage = cp; + +- locinfo->lc_handle[category] = lcid; ++ locinfo->lc_handle[category] = LocaleNameToLCID(sname, LOCALE_ALLOW_NEUTRAL_NAMES); + + set_lc_locale_name(locinfo, category); + + if(!locinfo->lc_category[category].locale) { ++ char buf[256]; + int len = 0; + +- if (lcid == MAKELANGID( LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK )) ++#if _MSVCR_VER < 110 ++ if (!wcscmp(sname, L"nn-NO") || !wcscmp(sname, L"nn")) + { + /* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */ +- strcpy( buf, "Norwegian-Nynorsk" ); +- len = strlen( buf ) + 1; ++ wcscpy( wbuf, L"Norwegian-Nynorsk" ); ++ len = wcslen( wbuf ) + 1; + } +- else len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256); +- buf[len-1] = '_'; +- len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY +- |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len); +- buf[len-1] = '.'; +- sprintf(buf+len, "%d", cp); +- len += strlen(buf+len); ++ else ++#endif ++ len += GetLocaleInfoEx(sname, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ wbuf[len-1] = '_'; ++ len += GetLocaleInfoEx(sname, LOCALE_SENGCOUNTRY ++ |LOCALE_NOUSEROVERRIDE, &wbuf[len], 256-len); ++ wbuf[len-1] = '.'; ++ swprintf(wbuf+len, 256 - len,L"%d", cp); ++ len += wcslen(wbuf+len); ++ ++ WideCharToMultiByte(cp, 0, wbuf, -1, buf, 256, NULL, NULL); + + return init_category_name(buf, len, locinfo, category); + } +@@ -1161,13 +1193,22 @@ void CDECL _free_locale(_locale_t locale) + } + + static inline BOOL category_needs_update(int cat, +- const threadlocinfo *locinfo, LCID lcid, unsigned short cp) ++ const threadlocinfo *locinfo, WCHAR *sname, unsigned short cp) + { ++#if _MSVCR_VER < 110 ++ LCID lcid; ++#endif + if(!locinfo) return TRUE; ++#if _MSVCR_VER >= 110 ++ if(!locinfo->lc_name[cat] || !sname) return TRUE; ++ return wcscmp(sname, locinfo->lc_name[cat]) != 0 || cp!=locinfo->lc_id[cat].wCodePage; ++#else ++ lcid = sname ? LocaleNameToLCID(sname, LOCALE_ALLOW_NEUTRAL_NAMES) : 0; + return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage; ++#endif + } + +-static __lc_time_data* create_time_data(LCID lcid) ++static __lc_time_data* create_time_data(WCHAR *sname) + { + static const DWORD time_data[] = { + LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, +@@ -1189,6 +1230,7 @@ static __lc_time_data* create_time_data(LCID lcid) + + __lc_time_data *cur; + int i, ret, size; ++ LCID lcid = LocaleNameToLCID(sname, LOCALE_ALLOW_NEUTRAL_NAMES); + + size = sizeof(__lc_time_data); + for(i=0; i<ARRAY_SIZE(time_data); i++) { +@@ -1198,14 +1240,14 @@ static __lc_time_data* create_time_data(LCID lcid) + size += ret; + + #if _MSVCR_VER == 0 || _MSVCR_VER >= 100 +- ret = GetLocaleInfoW(lcid, time_data[i], NULL, 0); ++ ret = GetLocaleInfoEx(sname, time_data[i], NULL, 0); + if(!ret) + return NULL; + size += ret*sizeof(wchar_t); + #endif + } + #if _MSVCR_VER >= 110 +- size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(wchar_t); ++ size += wcslen(sname)*sizeof(wchar_t); + #endif + + cur = malloc(size); +@@ -1220,13 +1262,13 @@ static __lc_time_data* create_time_data(LCID lcid) + #if _MSVCR_VER == 0 || _MSVCR_VER >= 100 + for(i=0; i<ARRAY_SIZE(time_data); i++) { + cur->wstr.wstr[i] = (wchar_t*)&cur->data[ret]; +- ret += GetLocaleInfoW(lcid, time_data[i], ++ ret += GetLocaleInfoEx(sname, time_data[i], + (wchar_t*)&cur->data[ret], size-ret)*sizeof(wchar_t); + } + #endif + #if _MSVCR_VER >= 110 + cur->locname = (wchar_t*)&cur->data[ret]; +- LCIDToLocaleName(lcid, (wchar_t*)&cur->data[ret], (size-ret)/sizeof(wchar_t), 0); ++ wcsncpy((wchar_t *) &cur->data[ret], sname, size-ret); + #else + cur->lcid = lcid; + #endif +@@ -1245,16 +1287,14 @@ static pthreadlocinfo create_locinfo(int category, + static const char numeric[] = "NUMERIC="; + static const char time[] = "TIME="; + +- pthreadlocinfo locinfo; +- LCID lcid[6] = { 0 }; ++ pthreadlocinfo locinfo = NULL; + unsigned short cp[6] = { 0 }; + const char *locale_name[6] = { 0 }; ++ WCHAR *locale_sname[6] = { 0 }; + int val, locale_len[6] = { 0 }; + char buf[256]; +- BOOL sname; +-#if _MSVCR_VER >= 100 ++ BOOL sname_match; + wchar_t wbuf[256]; +-#endif + int i; + + TRACE("(%d %s)\n", category, locale); +@@ -1263,7 +1303,7 @@ static pthreadlocinfo create_locinfo(int category, + return NULL; + + if(locale[0]=='C' && !locale[1]) { +- lcid[0] = 0; ++ locale_sname[0] = NULL; + cp[0] = CP_ACP; + } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') { + const char *p; +@@ -1286,30 +1326,36 @@ static pthreadlocinfo create_locinfo(int category, + i = LC_TIME; + locale += sizeof(time)-1; + } else +- return NULL; ++ goto fail; + + p = strchr(locale, ';'); + if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) { +- lcid[i] = 0; ++ locale_sname[i] = NULL; + cp[i] = CP_ACP; +- } else if(p) { +- memcpy(buf, locale, p-locale); +- buf[p-locale] = '\0'; +- lcid[i] = locale_to_LCID(buf, &cp[i], &sname); +- if(sname) { +- locale_name[i] = locale; +- locale_len[i] = p-locale; +- } + } else { +- lcid[i] = locale_to_LCID(locale, &cp[i], &sname); +- if(sname) { +- locale_name[i] = locale; +- locale_len[i] = strlen(locale); ++ BOOL locale_found = FALSE; ++ ++ if(p) { ++ memcpy(buf, locale, p-locale); ++ buf[p-locale] = '\0'; ++ locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); ++ locale_sname[i] = wcsdup(wbuf); ++ if(sname_match) { ++ locale_name[i] = locale; ++ locale_len[i] = p-locale; ++ } ++ } else { ++ locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); ++ locale_sname[i] = wcsdup(wbuf); ++ if(sname_match) { ++ locale_name[i] = locale; ++ locale_len[i] = strlen(locale); ++ } + } +- } + +- if(lcid[i] == -1) +- return NULL; ++ if(!locale_found || !locale_sname[i]) ++ goto fail; ++ } + + if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_') + break; +@@ -1317,16 +1363,25 @@ static pthreadlocinfo create_locinfo(int category, + locale = p+1; + } + } else { +- lcid[0] = locale_to_LCID(locale, &cp[0], &sname); +- if(lcid[0] == -1) ++ BOOL locale_found = locale_to_sname(locale, &cp[0], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); ++ ++ if(!locale_found) ++ return NULL; ++ ++ locale_sname[0] = wcsdup(wbuf); ++ if(!locale_sname[0]) + return NULL; +- if(sname) { ++ ++ if(sname_match) { + locale_name[0] = locale; + locale_len[0] = strlen(locale); + } + + for(i=1; i<6; i++) { +- lcid[i] = lcid[0]; ++ locale_sname[i] = wcsdup(locale_sname[0]); ++ if(!locale_sname[i]) ++ goto fail; ++ + cp[i] = cp[0]; + locale_name[i] = locale_name[0]; + locale_len[i] = locale_len[0]; +@@ -1336,18 +1391,51 @@ static pthreadlocinfo create_locinfo(int category, + for(i=1; i<6; i++) { + #if _MSVCR_VER < 140 + if(i==LC_CTYPE && cp[i]==CP_UTF8) { ++#if _MSVCR_VER >= 110 ++ if(old_locinfo) { ++ locale_sname[i] = wcsdup(old_locinfo->lc_name[i]); ++ if(old_locinfo->lc_name[i] && !locale_sname[i]) ++ goto fail; ++ } ++#else ++ int sname_size; ++ if(old_locinfo && old_locinfo->lc_handle[i]) { ++ sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, LOCALE_ALLOW_NEUTRAL_NAMES); ++ locale_sname[i] = malloc(sname_size * sizeof(WCHAR)); ++ if(!locale_sname[i]) ++ goto fail; ++ LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, LOCALE_ALLOW_NEUTRAL_NAMES); ++ } else { ++ locale_sname[i] = NULL; ++ } ++#endif ++ + locale_name[i] = NULL; + locale_len[i] = 0; +- lcid[i] = old_locinfo ? old_locinfo->lc_handle[i] : 0; + cp[i] = old_locinfo ? old_locinfo->lc_id[i].wCodePage : 0; + } + #endif + if(category!=LC_ALL && category!=i) { + if(old_locinfo) { +- lcid[i] = old_locinfo->lc_handle[i]; ++#if _MSVCR_VER >= 110 ++ locale_sname[i] = wcsdup(old_locinfo->lc_name[i]); ++ if(old_locinfo->lc_name[i] && !locale_sname[i]) ++ goto fail; ++#else ++ int sname_size; ++ if(old_locinfo->lc_handle[i]) { ++ sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, LOCALE_ALLOW_NEUTRAL_NAMES); ++ locale_sname[i] = malloc(sname_size * sizeof(WCHAR)); ++ if(!locale_sname[i]) ++ goto fail; ++ LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, LOCALE_ALLOW_NEUTRAL_NAMES); ++ } else { ++ locale_sname[i] = NULL; ++ } ++#endif + cp[i] = old_locinfo->lc_id[i].wCodePage; + } else { +- lcid[i] = 0; ++ locale_sname[i] = NULL; + cp[i] = 0; + } + } +@@ -1355,7 +1443,7 @@ static pthreadlocinfo create_locinfo(int category, + + locinfo = malloc(sizeof(threadlocinfo)); + if(!locinfo) +- return NULL; ++ goto fail; + + memset(locinfo, 0, sizeof(threadlocinfo)); + locinfo->refcount = 1; +@@ -1363,38 +1451,34 @@ static pthreadlocinfo create_locinfo(int category, + if(locale_name[LC_COLLATE] && + !init_category_name(locale_name[LC_COLLATE], + locale_len[LC_COLLATE], locinfo, LC_COLLATE)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + if(!category_needs_update(LC_COLLATE, old_locinfo, +- lcid[LC_COLLATE], cp[LC_COLLATE])) { ++ locale_sname[LC_COLLATE], cp[LC_COLLATE])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_COLLATE); + locinfo->lc_collate_cp = old_locinfo->lc_collate_cp; +- } else if(lcid[LC_COLLATE]) { +- if(!update_threadlocinfo_category(lcid[LC_COLLATE], ++ } else if(locale_sname[LC_COLLATE]) { ++ if(!update_threadlocinfo_category(locale_sname[LC_COLLATE], + cp[LC_COLLATE], locinfo, LC_COLLATE)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + locinfo->lc_collate_cp = locinfo->lc_id[LC_COLLATE].wCodePage; + } else { + if(!init_category_name("C", 1, locinfo, LC_COLLATE)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + } + + if(locale_name[LC_CTYPE] && + !init_category_name(locale_name[LC_CTYPE], + locale_len[LC_CTYPE], locinfo, LC_CTYPE)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + if(!category_needs_update(LC_CTYPE, old_locinfo, +- lcid[LC_CTYPE], cp[LC_CTYPE])) { ++ locale_sname[LC_CTYPE], cp[LC_CTYPE])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_CTYPE); + locinfo->lc_codepage = old_locinfo->lc_codepage; + locinfo->lc_clike = old_locinfo->lc_clike; +@@ -1406,28 +1490,25 @@ static pthreadlocinfo create_locinfo(int category, + locinfo->pcumap = old_locinfo->pcumap; + if(locinfo->ctype1_refcount) + InterlockedIncrement((LONG *)locinfo->ctype1_refcount); +- } else if(lcid[LC_CTYPE]) { ++ } else if(locale_sname[LC_CTYPE]) { + CPINFO cp_info; + int j; + +- if(!update_threadlocinfo_category(lcid[LC_CTYPE], ++ if(!update_threadlocinfo_category(locale_sname[LC_CTYPE], + cp[LC_CTYPE], locinfo, LC_CTYPE)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + locinfo->lc_codepage = locinfo->lc_id[LC_CTYPE].wCodePage; + locinfo->lc_clike = 1; + if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + locinfo->mb_cur_max = cp_info.MaxCharSize; + + locinfo->ctype1_refcount = malloc(sizeof(int)); + if(!locinfo->ctype1_refcount) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + *locinfo->ctype1_refcount = 1; + +@@ -1435,8 +1516,7 @@ static pthreadlocinfo create_locinfo(int category, + locinfo->pclmap = malloc(sizeof(char[256])); + locinfo->pcumap = malloc(sizeof(char[256])); + if(!locinfo->ctype1 || !locinfo->pclmap || !locinfo->pcumap) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + locinfo->ctype1[0] = 0; +@@ -1449,7 +1529,7 @@ static pthreadlocinfo create_locinfo(int category, + /* builtin GetStringTypeA doesn't set output to 0 on invalid input */ + locinfo->ctype1[i] = 0; + +- GetStringTypeA(lcid[LC_CTYPE], CT_CTYPE1, buf, ++ GetStringTypeA(locinfo->lc_handle[LC_CTYPE], CT_CTYPE1, buf, + 1, locinfo->ctype1+i); + } + +@@ -1464,9 +1544,9 @@ static pthreadlocinfo create_locinfo(int category, + buf[i] = i; + } + +- LCMapStringA(lcid[LC_CTYPE], LCMAP_LOWERCASE, buf, 256, ++ LCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_LOWERCASE, buf, 256, + (char*)locinfo->pclmap, 256); +- LCMapStringA(lcid[LC_CTYPE], LCMAP_UPPERCASE, buf, 256, ++ LCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_UPPERCASE, buf, 256, + (char*)locinfo->pcumap, 256); + } else { + locinfo->lc_clike = 1; +@@ -1475,20 +1555,19 @@ static pthreadlocinfo create_locinfo(int category, + locinfo->pclmap = cloc_clmap; + locinfo->pcumap = cloc_cumap; + if(!init_category_name("C", 1, locinfo, LC_CTYPE)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + } + + if(!category_needs_update(LC_MONETARY, old_locinfo, +- lcid[LC_MONETARY], cp[LC_MONETARY]) && ++ locale_sname[LC_MONETARY], cp[LC_MONETARY]) && + !category_needs_update(LC_NUMERIC, old_locinfo, +- lcid[LC_NUMERIC], cp[LC_NUMERIC])) { ++ locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) { + locinfo->lconv = old_locinfo->lconv; + locinfo->lconv_intl_refcount = old_locinfo->lconv_intl_refcount; + if(locinfo->lconv_intl_refcount) + InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount); +- } else if(lcid[LC_MONETARY] || lcid[LC_NUMERIC]) { ++ } else if(locale_sname[LC_MONETARY] || locale_sname[LC_NUMERIC]) { + locinfo->lconv = malloc(sizeof(struct lconv)); + locinfo->lconv_intl_refcount = malloc(sizeof(int)); + if(!locinfo->lconv || !locinfo->lconv_intl_refcount) { +@@ -1496,8 +1575,7 @@ static pthreadlocinfo create_locinfo(int category, + free(locinfo->lconv_intl_refcount); + locinfo->lconv = NULL; + locinfo->lconv_intl_refcount = NULL; +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + memset(locinfo->lconv, 0, sizeof(struct lconv)); + *locinfo->lconv_intl_refcount = 1; +@@ -1508,12 +1586,11 @@ static pthreadlocinfo create_locinfo(int category, + if(locale_name[LC_MONETARY] && + !init_category_name(locale_name[LC_MONETARY], + locale_len[LC_MONETARY], locinfo, LC_MONETARY)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + if(!category_needs_update(LC_MONETARY, old_locinfo, +- lcid[LC_MONETARY], cp[LC_MONETARY])) { ++ locale_sname[LC_MONETARY], cp[LC_MONETARY])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_MONETARY); + locinfo->lconv_mon_refcount = old_locinfo->lconv_mon_refcount; + if(locinfo->lconv_mon_refcount) +@@ -1543,59 +1620,58 @@ static pthreadlocinfo create_locinfo(int category, + locinfo->lconv->_W_negative_sign = old_locinfo->lconv->_W_negative_sign; + #endif + } +- } else if(lcid[LC_MONETARY]) { +- if(!update_threadlocinfo_category(lcid[LC_MONETARY], ++ } else if(locale_sname[LC_MONETARY]) { ++ if(!update_threadlocinfo_category(locale_sname[LC_MONETARY], + cp[LC_MONETARY], locinfo, LC_MONETARY)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + locinfo->lconv_mon_refcount = malloc(sizeof(int)); + if(!locinfo->lconv_mon_refcount) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + *locinfo->lconv_mon_refcount = 1; + +- i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->int_curr_symbol = malloc(i))) +- memcpy(locinfo->lconv->int_curr_symbol, buf, i); ++ WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->int_curr_symbol, i, NULL, NULL); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SCURRENCY +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->currency_symbol = malloc(i))) +- memcpy(locinfo->lconv->currency_symbol, buf, i); ++ WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->currency_symbol, i, NULL, NULL); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->mon_decimal_point = malloc(i))) +- memcpy(locinfo->lconv->mon_decimal_point, buf, i); ++ WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_decimal_point, i, NULL, NULL); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->mon_thousands_sep = malloc(i))) +- memcpy(locinfo->lconv->mon_thousands_sep, buf, i); ++ WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_thousands_sep, i, NULL, NULL); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONGROUPING +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONGROUPING ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ WideCharToMultiByte(CP_ACP, 0, wbuf, -1, buf, 256, NULL, NULL); + if(i>1) + i = i/2 + (buf[i-2]=='0'?0:1); + if(i && (locinfo->lconv->mon_grouping = malloc(i))) { +@@ -1605,145 +1681,130 @@ static pthreadlocinfo create_locinfo(int category, + if(buf[i] != '0') + locinfo->lconv->mon_grouping[i/2+1] = 127; + } else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->positive_sign = malloc(i))) +- memcpy(locinfo->lconv->positive_sign, buf, i); ++ WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->positive_sign, i, NULL, NULL); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->negative_sign = malloc(i))) +- memcpy(locinfo->lconv->negative_sign, buf, i); ++ WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->negative_sign, i, NULL, NULL); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IINTLCURRDIGITS ++ if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IINTLCURRDIGITS + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->int_frac_digits = val; + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_ICURRDIGITS ++ if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_ICURRDIGITS + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->frac_digits = val; + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSYMPRECEDES ++ if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSYMPRECEDES + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->p_cs_precedes = val; + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSEPBYSPACE ++ if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSEPBYSPACE + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->p_sep_by_space = val; + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSYMPRECEDES ++ if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSYMPRECEDES + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->n_cs_precedes = val; + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSEPBYSPACE ++ if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSEPBYSPACE + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->n_sep_by_space = val; + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSIGNPOSN ++ if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSIGNPOSN + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->p_sign_posn = val; + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSIGNPOSN ++ if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSIGNPOSN + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->n_sign_posn = val; + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + #if _MSVCR_VER >= 100 +- i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_int_curr_symbol = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(wchar_t)); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SCURRENCY ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_currency_symbol = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(wchar_t)); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_mon_decimal_point = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(wchar_t)); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_mon_thousands_sep = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(wchar_t)); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_positive_sign = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(wchar_t)); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN ++ i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_negative_sign = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(wchar_t)); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + #endif + } else { +@@ -1775,20 +1836,18 @@ static pthreadlocinfo create_locinfo(int category, + } + + if(!init_category_name("C", 1, locinfo, LC_MONETARY)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + } + + if(locale_name[LC_NUMERIC] && + !init_category_name(locale_name[LC_NUMERIC], + locale_len[LC_NUMERIC], locinfo, LC_NUMERIC)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + if(!category_needs_update(LC_NUMERIC, old_locinfo, +- lcid[LC_NUMERIC], cp[LC_NUMERIC])) { ++ locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_NUMERIC); + locinfo->lconv_num_refcount = old_locinfo->lconv_num_refcount; + if(locinfo->lconv_num_refcount) +@@ -1802,41 +1861,40 @@ static pthreadlocinfo create_locinfo(int category, + locinfo->lconv->_W_thousands_sep = old_locinfo->lconv->_W_thousands_sep; + #endif + } +- } else if(lcid[LC_NUMERIC]) { +- if(!update_threadlocinfo_category(lcid[LC_NUMERIC], ++ } else if(locale_sname[LC_NUMERIC]) { ++ if(!update_threadlocinfo_category(locale_sname[LC_NUMERIC], + cp[LC_NUMERIC], locinfo, LC_NUMERIC)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + locinfo->lconv_num_refcount = malloc(sizeof(int)); + if(!locinfo->lconv_num_refcount) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + *locinfo->lconv_num_refcount = 1; + +- i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SDECIMAL +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->decimal_point = malloc(i))) +- memcpy(locinfo->lconv->decimal_point, buf, i); ++ WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->decimal_point, i, NULL, NULL); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_STHOUSAND +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->thousands_sep = malloc(i))) +- memcpy(locinfo->lconv->thousands_sep, buf, i); ++ WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->thousands_sep, i, NULL, NULL); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SGROUPING +- |LOCALE_NOUSEROVERRIDE, buf, 256); ++ i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SGROUPING ++ |LOCALE_NOUSEROVERRIDE, wbuf, 256); ++ WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, buf, 256, NULL, NULL); + if(i>1) + i = i/2 + (buf[i-2]=='0'?0:1); + if(i && (locinfo->lconv->grouping = malloc(i))) { +@@ -1846,27 +1904,24 @@ static pthreadlocinfo create_locinfo(int category, + if(buf[i] != '0') + locinfo->lconv->grouping[i/2+1] = 127; + } else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + #if _MSVCR_VER >= 100 +- i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_SDECIMAL ++ i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_decimal_point = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(wchar_t)); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_STHOUSAND ++ i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_thousands_sep = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(wchar_t)); + else { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + #endif + } else { +@@ -1882,45 +1937,48 @@ static pthreadlocinfo create_locinfo(int category, + } + + if (!init_category_name("C", 1, locinfo, LC_NUMERIC)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + } + + if(locale_name[LC_TIME] && + !init_category_name(locale_name[LC_TIME], + locale_len[LC_TIME], locinfo, LC_TIME)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + + if(!category_needs_update(LC_TIME, old_locinfo, +- lcid[LC_TIME], cp[LC_TIME])) { ++ locale_sname[LC_TIME], cp[LC_TIME])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_TIME); + locinfo->lc_time_curr = old_locinfo->lc_time_curr; + InterlockedIncrement(&locinfo->lc_time_curr->refcount); +- } else if(lcid[LC_TIME]) { +- if(!update_threadlocinfo_category(lcid[LC_TIME], ++ } else if(locale_sname[LC_TIME]) { ++ if(!update_threadlocinfo_category(locale_sname[LC_TIME], + cp[LC_TIME], locinfo, LC_TIME)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + +- locinfo->lc_time_curr = create_time_data(lcid[LC_TIME]); ++ locinfo->lc_time_curr = create_time_data(locale_sname[LC_TIME]); + if(!locinfo->lc_time_curr) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + } else { + if(!init_category_name("C", 1, locinfo, LC_TIME)) { +- free_locinfo(locinfo); +- return NULL; ++ goto fail; + } + locinfo->lc_time_curr = &cloc_time_data; + InterlockedIncrement(&locinfo->lc_time_curr->refcount); + } + + return locinfo; ++ ++fail: ++ free_locinfo(locinfo); ++ ++ for (i = 0; i < LC_MAX; i++) ++ free(locale_sname[i]); ++ ++ return NULL; + } + + /********************************************************************* +diff --git a/dlls/msvcrt/mbcs.c b/dlls/msvcrt/mbcs.c +index 16c5c378be7..8598bceb029 100644 +--- a/dlls/msvcrt/mbcs.c ++++ b/dlls/msvcrt/mbcs.c +@@ -252,8 +252,9 @@ threadmbcinfo* create_mbcinfo(int cp, LCID lcid, threadmbcinfo *old_mbcinfo) + } + + if(lcid == -1) { ++ WCHAR wbuf[LOCALE_NAME_MAX_LENGTH]; + sprintf(bufA, ".%d", newcp); +- mbcinfo->mblcid = locale_to_LCID(bufA, NULL, NULL); ++ mbcinfo->mblcid = locale_to_sname(bufA, NULL, NULL, wbuf, LOCALE_NAME_MAX_LENGTH) ? LocaleNameToLCID(wbuf, LOCALE_ALLOW_NEUTRAL_NAMES) : -1; + } else { + mbcinfo->mblcid = lcid; + } +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index 1d2db6a4d87..42c41c2200c 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -170,8 +170,7 @@ struct __thread_data { + int processing_throw; + frame_info *frame_info_head; + void *unk8[6]; +- LCID cached_lcid; +- BOOL cached_sname; ++ WCHAR cached_sname[LOCALE_NAME_MAX_LENGTH]; + int unk9[2]; + DWORD cached_cp; + char cached_locale[131]; +@@ -186,7 +185,7 @@ typedef struct __thread_data thread_data_t; + + extern thread_data_t *CDECL msvcrt_get_thread_data(void) DECLSPEC_HIDDEN; + +-LCID locale_to_LCID(const char*, unsigned short*, BOOL*) DECLSPEC_HIDDEN; ++BOOL locale_to_sname(const char*, unsigned short*, BOOL*, WCHAR*, int) DECLSPEC_HIDDEN; + extern _locale_t MSVCRT_locale DECLSPEC_HIDDEN; + extern __lc_time_data cloc_time_data DECLSPEC_HIDDEN; + extern unsigned int MSVCRT___lc_codepage; +diff --git a/nls/locale.nls b/nls/locale.nls +index bc028035a8b..a1f32fdfde7 100644 +Binary files a/nls/locale.nls and b/nls/locale.nls differ +diff --git a/tools/make_unicode b/tools/make_unicode +index 69dce31df47..de34588359c 100755 +--- a/tools/make_unicode ++++ b/tools/make_unicode +@@ -4678,6 +4678,7 @@ sub build_locale_data() + my $snativectryname = loc_query( $loc, "/ldml/localeDisplayNames/territories/territory[@type='$territory' and not(@alt)]" ); + $sengcountry =~ s/South Korea/Korea/; + $sengcountry =~ s/T\xfcrkiye/Turkey/; ++ $sengcountry =~ s/Hong Kong SAR China/Hong Kong SAR/; + $snativelangname ||= $senglanguage; + $snativectryname ||= $sengcountry; + if ($script)
v3: * Changed the test asm * Squashed the test and fix patch * Renamed wine_i386_rewind to rewind_preserve_stack * Added CFI annotations to rewind_preserve_stack * Added comment specifying why that function is needed anyway
I'm not familiar with CFI stuff so if I'm missing something please let me know.