More NLS updates

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

This is the next set of kernel NLS updates.

Cheers,
Jon

Changelog:

  Jon Griffiths <jon_p_griffiths@yahoo.com>
  +dlls/kernel/kernel32.spec dlls/kernel/locale.c
    Add LanguageGroup/GeoID enumeration fns
    Add some reg entries from XP
    SetLocaleInfo(): Set 2 I-values from S-values
    Optimise code size, fix a few bugs, documentation




__________________________________
Do you Yahoo!?
Yahoo! SiteBuilder - Free, easy-to-use web site design software
http://sitebuilder.yahoo.com
diff -u wine/dlls/kernel/locale.c wine-develop-old/dlls/kernel/locale.c
--- wine/dlls/kernel/locale.c	2003-09-17 14:37:19.000000000 +0100
+++ wine-develop-old/dlls/kernel/locale.c	2003-09-20 23:50:44.000000000 +0100
@@ -46,6 +46,8 @@
 
 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|LOCALE_RETURN_NUMBER)
 
+static const WCHAR kernel32W[] = { 'K','E','R','N','E','L','3','2','\0' };
+
 extern void CODEPAGE_Init( UINT ansi_cp, UINT oem_cp, UINT mac_cp, UINT unix_cp, LCID lcid );
 
 /* Charset to codepage map, sorted by name. */
@@ -163,11 +165,35 @@
  */
 inline static void update_registry( LCID lcid )
 {
+    static const USHORT updateValues[] = {
+      LOCALE_SLANGUAGE,
+      LOCALE_SCOUNTRY, LOCALE_ICOUNTRY,
+      LOCALE_S1159, LOCALE_S2359,
+      LOCALE_STIME, LOCALE_ITIME,
+      LOCALE_ITLZERO,
+      LOCALE_SSHORTDATE,
+      LOCALE_IDATE,
+      LOCALE_SLONGDATE,
+      LOCALE_SDATE,
+      LOCALE_SCURRENCY, LOCALE_ICURRENCY,
+      LOCALE_INEGCURR,
+      LOCALE_ICURRDIGITS,
+      LOCALE_SDECIMAL,
+      LOCALE_SLIST,
+      LOCALE_STHOUSAND,
+      LOCALE_IDIGITS,
+      LOCALE_IDIGITSUBSTITUTION,
+      LOCALE_SNATIVEDIGITS,
+      LOCALE_ITIMEMARKPOSN,
+      LOCALE_ICALENDARTYPE,
+      LOCALE_ILZERO,
+      LOCALE_IMEASURE
+    };
     static const WCHAR LocaleW[] = {'L','o','c','a','l','e',0};
     UNICODE_STRING nameW;
     char buffer[20];
     WCHAR bufferW[80];
-    DWORD count = sizeof(buffer);
+    DWORD count, i;
     HKEY hkey;
 
     if (!(hkey = create_registry_key()))
@@ -177,51 +203,30 @@
     count = sizeof(bufferW);
     if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, (LPBYTE)bufferW, count, &count))
     {
-        KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
-        RtlUnicodeToMultiByteN( buffer, sizeof(buffer)-1, &count,
-                                (WCHAR *)info->Data, info->DataLength );
-        buffer[count] = 0;
-        if (strtol( buffer, NULL, 16 ) == lcid)  /* already set correctly */
+        const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
+        LPCWSTR szValueText = (LPCWSTR)info->Data;
+
+        if (strtoulW( szValueText, NULL, 16 ) == lcid)  /* already set correctly */
         {
             NtClose( hkey );
             return;
         }
-        TRACE( "updating registry, locale changed %s -> %08lx\n", buffer, lcid );
+        TRACE( "updating registry, locale changed %s -> %08lx\n", debugstr_w(szValueText), lcid );
     }
     else TRACE( "updating registry, locale changed none -> %08lx\n", lcid );
 
     sprintf( buffer, "%08lx", lcid );
-    RtlMultiByteToUnicodeN( bufferW, sizeof(bufferW), NULL, buffer, strlen(buffer)+1 );
-    NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW)+1) * sizeof(WCHAR) );
+    /* Note: '9' constant below is strlen(buffer) + 1 */
+    RtlMultiByteToUnicodeN( bufferW, sizeof(bufferW), NULL, buffer, 9 );
+    NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, 9 * sizeof(WCHAR) );
     NtClose( hkey );
 
-#define UPDATE_VALUE(lctype) do { \
-    GetLocaleInfoW( lcid, (lctype)|LOCALE_NOUSEROVERRIDE, bufferW, sizeof(bufferW)/sizeof(WCHAR) ); \
-    SetLocaleInfoW( lcid, (lctype), bufferW ); } while (0)
-
-    UPDATE_VALUE(LOCALE_SLANGUAGE);
-    UPDATE_VALUE(LOCALE_SCOUNTRY);
-    UPDATE_VALUE(LOCALE_ICOUNTRY);
-    UPDATE_VALUE(LOCALE_S1159);
-    UPDATE_VALUE(LOCALE_S2359);
-    UPDATE_VALUE(LOCALE_STIME);
-    UPDATE_VALUE(LOCALE_ITIME);
-    UPDATE_VALUE(LOCALE_ITLZERO);
-    UPDATE_VALUE(LOCALE_SSHORTDATE);
-    UPDATE_VALUE(LOCALE_IDATE);
-    UPDATE_VALUE(LOCALE_SLONGDATE);
-    UPDATE_VALUE(LOCALE_SDATE);
-    UPDATE_VALUE(LOCALE_SCURRENCY);
-    UPDATE_VALUE(LOCALE_ICURRENCY);
-    UPDATE_VALUE(LOCALE_INEGCURR);
-    UPDATE_VALUE(LOCALE_ICURRDIGITS);
-    UPDATE_VALUE(LOCALE_SDECIMAL);
-    UPDATE_VALUE(LOCALE_SLIST);
-    UPDATE_VALUE(LOCALE_STHOUSAND);
-    UPDATE_VALUE(LOCALE_IDIGITS);
-    UPDATE_VALUE(LOCALE_ILZERO);
-    UPDATE_VALUE(LOCALE_IMEASURE);
-#undef UPDATE_VALUE
+    for (i = 0; i < sizeof(updateValues)/sizeof(updateValues[0]); i++)
+    {
+        GetLocaleInfoW( lcid, updateValues[i] | LOCALE_NOUSEROVERRIDE, bufferW,
+                        sizeof(bufferW)/sizeof(WCHAR) );
+        SetLocaleInfoW( lcid, updateValues[i], bufferW );
+    }
 }
 
 
@@ -252,9 +257,9 @@
                    buf_country, sizeof(buf_country));
     TRACE("LOCALE_SISO3166CTRYNAME: %s\n", buf_country);
 
-    if(l_data->lang && strlen(l_data->lang) > 0 && !strcasecmp(l_data->lang, buf_language))
+    if(l_data->lang[0] && !strcasecmp(l_data->lang, buf_language))
     {
-        if(l_data->country && strlen(l_data->country) > 0)
+        if(l_data->country[0])
         {
             if(!strcasecmp(l_data->country, buf_country))
             {
@@ -286,7 +291,7 @@
                    buf_en_language, sizeof(buf_en_language));
     TRACE("LOCALE_SENGLANGUAGE: %s\n", buf_en_language);
 
-    if(l_data->lang && strlen(l_data->lang) > 0 && !strcasecmp(l_data->lang, buf_en_language))
+    if(l_data->lang[0] && !strcasecmp(l_data->lang, buf_en_language))
     {
         l_data->found_lang_id[l_data->n_found] = LangID;
         strncpy(l_data->found_country[l_data->n_found], buf_country, 3);
@@ -320,6 +325,7 @@
 {
     LANG_FIND_DATA l_data;
     char lang_string[256];
+    HMODULE hKernel32;
 
     if(!Lang)
     {
@@ -330,14 +336,16 @@
     memset(&l_data, 0, sizeof(LANG_FIND_DATA));
     strncpy(l_data.lang, Lang, sizeof(l_data.lang));
 
-    if(Country && strlen(Country) > 0)
+    if(Country && Country[0])
         strncpy(l_data.country, Country, sizeof(l_data.country));
 
-    EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
+    hKernel32 = GetModuleHandleW(kernel32W);
+
+    EnumResourceLanguagesA(hKernel32, (LPSTR)RT_STRING,
                            (LPCSTR)LOCALE_ILANGUAGE, find_language_id_proc, (LPARAM)&l_data);
 
     strcpy(lang_string, l_data.lang);
-    if(l_data.country && strlen(l_data.country) > 0)
+    if(l_data.country[0])
     {
         strcat(lang_string, "_");
         strcat(lang_string, l_data.country);
@@ -345,18 +353,18 @@
 
     if(!l_data.n_found)
     {
-        if(l_data.country && strlen(l_data.country) > 0)
+        if(l_data.country[0])
         {
             MESSAGE("Warning: Language '%s' was not found, retrying without country name...\n", lang_string);
             l_data.country[0] = 0;
-            EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
+            EnumResourceLanguagesA(hKernel32, (LPSTR)RT_STRING,
                                    (LPCSTR)LOCALE_ILANGUAGE, find_language_id_proc, (LONG)&l_data);
         }
     }
 
     /* Re-evaluate lang_string */
     strcpy(lang_string, l_data.lang);
-    if(l_data.country && strlen(l_data.country) > 0)
+    if(l_data.country[0])
     {
         strcat(lang_string, "_");
         strcat(lang_string, l_data.country);
@@ -428,7 +436,7 @@
             {
                 const struct charset_entry *entry;
                 char charset_name[16];
-                int i, j;
+                size_t i, j;
 
                 /* remove punctuation characters from charset name */
                 for (i = j = 0; charset[i] && j < sizeof(charset_name)-1; i++)
@@ -481,6 +489,7 @@
     static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
     static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
     static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
+    static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
     static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
     static const WCHAR s1159W[] = {'s','1','1','5','9',0};
     static const WCHAR s2359W[] = {'s','2','3','5','9',0};
@@ -495,6 +504,7 @@
     static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
     static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
     static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
+    static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
     static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
     static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
     static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
@@ -502,6 +512,7 @@
     static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
     static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
     static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
+    static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
 
     switch (lctype & ~LOCALE_LOCALEINFOFLAGSMASK)
     {
@@ -548,6 +559,11 @@
     case LOCALE_ITLZERO:          return iTLZeroW;
     case LOCALE_SCOUNTRY:         return sCountryW;
     case LOCALE_SLANGUAGE:        return sLanguageW;
+
+    /* The following are used in XP and later */
+    case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
+    case LOCALE_SNATIVEDIGITS:      return sNativeDigitsW;
+    case LOCALE_ITIMEMARKPOSN:      return iTimePrefixW;
     }
     return NULL;
 }
@@ -559,7 +575,7 @@
  * Retrieve user-modified locale info from the registry.
  * Return length, 0 on error, -1 if not found.
  */
-static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
+static inline INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
 {
     DWORD size;
     INT ret;
@@ -621,12 +637,23 @@
 /******************************************************************************
  *		GetLocaleInfoA (KERNEL32.@)
  *
- * NOTES
- *  LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
+ * Get information about an aspect of a locale.
+ *
+ * PARAMS
+ *  lcid   [I] LCID of the locale
+ *  lctype [I] LCTYPE_ flags from "winnls.h"
+ *  buffer [O] Destination for the information
+ *  len    [I] Length of buffer in characters
  *
- *  MS online documentation states that the string returned is NULL terminated
- *  except for LOCALE_FONTSIGNATURE  which "will return a non-NULL
- *  terminated string".
+ * RETURNS
+ *  Success: The size of the data requested. If buffer is non-NULL, it is filled
+ *           with the information.
+ *  Failure: 0. Use GetLastError() to determine the cause.
+ *
+ * NOTES
+ *  - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
+ *  - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
+ *    which is a bit string.
  */
 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
 {
@@ -679,12 +706,7 @@
 /******************************************************************************
  *		GetLocaleInfoW (KERNEL32.@)
  *
- * NOTES
- *  LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
- *
- *  MS online documentation states that the string returned is NULL terminated
- *  except for LOCALE_FONTSIGNATURE  which "will return a non-NULL
- *  terminated string".
+ * See GetLocaleInfoA.
  */
 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
 {
@@ -695,7 +717,7 @@
     INT ret;
     UINT lcflags;
     const WCHAR *p;
-    int i;
+    unsigned int i;
 
     if (len < 0 || (len && !buffer))
     {
@@ -704,8 +726,7 @@
     }
     if (!len) buffer = NULL;
 
-    if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
-    else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
+    lcid = ConvertDefaultLocale(lcid);
 
     lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
     lctype &= ~LOCALE_LOCALEINFOFLAGSMASK;
@@ -727,7 +748,7 @@
     if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
         lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
 
-    hModule = GetModuleHandleA( "kernel32.dll" );
+    hModule = GetModuleHandleW( kernel32W );
     if (!(hrsrc = FindResourceExW( hModule, (LPWSTR)RT_STRING, (LPCWSTR)((lctype >> 4) + 1), lang_id )))
     {
         SetLastError( ERROR_INVALID_FLAGS );  /* no such lctype */
@@ -784,6 +805,27 @@
 
 /******************************************************************************
  *		SetLocaleInfoA	[KERNEL32.@]
+ *
+ * Set information about an aspect of a locale.
+ *
+ * PARAMS
+ *  lcid   [I] LCID of the locale
+ *  lctype [I] LCTYPE_ flags from "winnls.h"
+ *  data   [I] Information to set
+ *
+ * RETURNS
+ *  Success: TRUE. The information given will be returned by GetLocaleInfoA()
+ *           whenever it is called without LOCALE_NOUSEROVERRIDE.
+ *  Failure: FALSE. Use GetLastError() to determine the cause.
+ *
+ * NOTES
+ *  - Values are only be set for the current user locale; the system locale
+ *  settings cannot be changed.
+ *  - Any settings changed by this call are lost when the locale is changed by
+ *  the control panel (in Wine, this happens every time you change LANG).
+ *  - The native implementation of this function does not check that lcid matches
+ *  the current user locale, and simply sets the new values. Wine warns you in
+ *  this case, but behaves the same.
  */
 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
 {
@@ -792,8 +834,7 @@
     DWORD len;
     BOOL ret;
 
-    if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
-    else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
+    lcid = ConvertDefaultLocale(lcid);
 
     if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
     len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
@@ -811,6 +852,8 @@
 
 /******************************************************************************
  *		SetLocaleInfoW	(KERNEL32.@)
+ *
+ * See SetLocaleInfoA.
  */
 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
 {
@@ -820,15 +863,29 @@
     NTSTATUS status;
     HKEY hkey;
 
-    if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
-    else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
+    lcid = ConvertDefaultLocale(lcid);
+
+    lctype &= ~LOCALE_LOCALEINFOFLAGSMASK;
+    value = get_locale_value_name( lctype );
 
-    if (!(value = get_locale_value_name( lctype )))
+    if (!data || !value)
     {
         SetLastError( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
-    if (lcid != GetUserDefaultLCID()) return TRUE;  /* fake success */
+
+    if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
+    {
+        SetLastError( ERROR_INVALID_FLAGS );
+        return FALSE;
+    }
+
+    if (lcid != GetUserDefaultLCID())
+    {
+        /* Windows does not check that the lcid matches the current lcid */
+        WARN("locale 0x%08lx isn't the current locale (0x%08lx), setting anyway!\n",
+             lcid, GetUserDefaultLCID());
+    }
 
     TRACE("setting %lx to %s\n", lctype, debugstr_w(data) );
 
@@ -840,16 +897,61 @@
     if (!(hkey = create_registry_key())) return FALSE;
     RtlInitUnicodeString( &valueW, value );
     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
+
+    if (lctype == LOCALE_SDATE || lctype == LOCALE_SLONGDATE)
+    {
+      /* Set I-value from S value */
+      WCHAR *lpD, *lpM, *lpY;
+      WCHAR szBuff[2];
+
+      lpD = strchrW(data, 'd');
+      lpM = strchrW(data, 'M');
+      lpY = strchrW(data, 'y');
+
+      if (lpD <= lpM)
+      {
+        szBuff[0] = '1'; /* D-M-Y */
+      }
+      else
+      {
+        if (lpY <= lpM)
+          szBuff[0] = '2'; /* Y-M-D */
+        else
+          szBuff[0] = '0'; /* M-D-Y */
+      }
+
+      szBuff[1] = '\0';
+
+      if (lctype == LOCALE_SDATE)
+        lctype = LOCALE_IDATE;
+      else
+        lctype = LOCALE_ILDATE;
+
+      value = get_locale_value_name( lctype );
+
+      WriteProfileStringW( intlW, value, szBuff );
+
+      RtlInitUnicodeString( &valueW, value );
+      status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
+    }
+
     NtClose( hkey );
 
     if (status) SetLastError( RtlNtStatusToDosError(status) );
     return !status;
-    return TRUE;
 }
 
 
 /***********************************************************************
  *           GetThreadLocale    (KERNEL32.@)
+ *
+ * Get the current threads locale.
+ *
+ * PARAMS
+ *  None.
+ *
+ * RETURNS
+ *  The LCID currently assocated with the calling thread.
  */
 LCID WINAPI GetThreadLocale(void)
 {
@@ -858,49 +960,100 @@
     return ret;
 }
 
-
 /**********************************************************************
  *           SetThreadLocale    (KERNEL32.@)
  *
- * FIXME
- *  check if lcid is a valid cp
+ * Set the current threads locale.
+ *
+ * PARAMS
+ *  lcid [I] LCID of the locale to set
+ *
+ * RETURNS
+ *  Success: TRUE. The threads locale is set to lcid.
+ *  Failure: FALSE. Use GetLastError() to determine the cause.
  */
-BOOL WINAPI SetThreadLocale( LCID lcid ) /* [in] Locale identifier */
+BOOL WINAPI SetThreadLocale( LCID lcid )
 {
-    if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
-    else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
+    TRACE("(0x%04lX)\n", lcid);
+
+    lcid = ConvertDefaultLocale(lcid);
+
+    if (lcid != GetThreadLocale())
+    {
+        if (!IsValidLocale(lcid, LCID_SUPPORTED))
+        {
+            SetLastError(ERROR_INVALID_PARAMETER);
+            return FALSE;
+        }
 
-    NtCurrentTeb()->CurrentLocale = lcid;
-    NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
+        NtCurrentTeb()->CurrentLocale = lcid;
+        NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
+    }
     return TRUE;
 }
 
-
 /******************************************************************************
  *		ConvertDefaultLocale (KERNEL32.@)
+ *
+ * Convert a default locale identifier into a real identifier.
+ *
+ * PARAMS
+ *  lcid [I] LCID identifier of the locale to convert
+ *
+ * RETURNS
+ *  lcid unchanged, if not a default locale or is its sublanguage is
+ *   not SUBLANG_NEUTRAL.
+ *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
+ *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
+ *  Otherwise, lcid with sublanguage cheanged to SUBLANG_DEFAULT.
  */
 LCID WINAPI ConvertDefaultLocale( LCID lcid )
 {
+    LANGID langid;
+
     switch (lcid)
     {
     case LOCALE_SYSTEM_DEFAULT:
-        return GetSystemDefaultLCID();
+        lcid = GetSystemDefaultLCID();
+        break;
     case LOCALE_USER_DEFAULT:
-        return GetUserDefaultLCID();
     case LOCALE_NEUTRAL:
-        return MAKELCID (LANG_NEUTRAL, SUBLANG_NEUTRAL);
+        lcid = GetUserDefaultLCID();
+        break;
+    default:
+        /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
+        langid = LANGIDFROMLCID(lcid);
+        if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
+        {
+          langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
+          lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
+        }
     }
-    return MAKELANGID( PRIMARYLANGID(lcid), SUBLANG_NEUTRAL);
+    return lcid;
 }
 
 
 /******************************************************************************
  *           IsValidLocale   (KERNEL32.@)
+ *
+ * Determine if a locale is valid.
+ *
+ * PARAMS
+ *  lcid  [I] LCID of the locale to check
+ *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
+ *
+ * RETURN
+ *  TRUE,  if lcid is valid,
+ *  FALSE, otherwise.
+ *
+ * NOTES
+ *  Wine does not currently make the distinction between supported and installed. All
+ *  languages supported are installed by default.
  */
 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
 {
     /* check if language is registered in the kernel32 resources */
-    return FindResourceExW( GetModuleHandleA("KERNEL32"), (LPWSTR)RT_STRING,
+    return FindResourceExW( GetModuleHandleW(kernel32W), (LPWSTR)RT_STRING,
                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
 }
 
@@ -927,12 +1080,21 @@
 
 /******************************************************************************
  *           EnumSystemLocalesA  (KERNEL32.@)
+ *
+ * Call a users function for each locale available on the system.
+ *
+ * PARAMS
+ *  lpfnLocaleEnum [I] Callback function to call for each locale
+ *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE. Use GetLastError() to determine the cause.
  */
-BOOL WINAPI EnumSystemLocalesA(LOCALE_ENUMPROCA lpfnLocaleEnum,
-                                   DWORD flags)
+BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
 {
-    TRACE("(%p,%08lx)\n", lpfnLocaleEnum,flags);
-    EnumResourceLanguagesA( GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
+    TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
+    EnumResourceLanguagesA( GetModuleHandleW(kernel32W), (LPSTR)RT_STRING,
                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
                             (LONG)lpfnLocaleEnum);
     return TRUE;
@@ -941,11 +1103,13 @@
 
 /******************************************************************************
  *           EnumSystemLocalesW  (KERNEL32.@)
+ *
+ * See EnumSystemLocalesA.
  */
-BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD flags )
+BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
 {
-    TRACE("(%p,%08lx)\n", lpfnLocaleEnum,flags);
-    EnumResourceLanguagesW( GetModuleHandleA("KERNEL32"), (LPWSTR)RT_STRING,
+    TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
+    EnumResourceLanguagesW( GetModuleHandleW(kernel32W), (LPWSTR)RT_STRING,
                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
                             (LONG)lpfnLocaleEnum);
     return TRUE;
@@ -954,6 +1118,18 @@
 
 /***********************************************************************
  *           VerLanguageNameA  (KERNEL32.@)
+ *
+ * Get the name of a language.
+ *
+ * PARAMS
+ *  wLang  [I] LANGID of the language
+ *  szLang [O] Destination for the language name
+ *
+ * RETURNS
+ *  Success: The size of the language name. If szLang is non-NULL, it is filled
+ *           with the name.
+ *  Failure: 0. Use GetLastError() to determine the cause.
+ *
  */
 DWORD WINAPI VerLanguageNameA( UINT wLang, LPSTR szLang, UINT nSize )
 {
@@ -963,6 +1139,8 @@
 
 /***********************************************************************
  *           VerLanguageNameW  (KERNEL32.@)
+ *
+ * See VerLanguageNameA.
  */
 DWORD WINAPI VerLanguageNameW( UINT wLang, LPWSTR szLang, UINT nSize )
 {
@@ -1107,8 +1285,7 @@
 
     if (!dstlen) dst = NULL;
 
-    if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
-    else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
+    lcid = ConvertDefaultLocale(lcid);
 
     if (flags & LCMAP_SORTKEY)
     {
@@ -1215,8 +1392,7 @@
         return 0;
     }
 
-    GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
-                   (WCHAR *)&locale_cp, (sizeof(locale_cp)/sizeof(WCHAR)));
+    locale_cp = get_lcid_codepage(lcid);
 
     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 128);
     if (srclenW)
@@ -1316,8 +1492,7 @@
     if (len1 < 0) len1 = strlen(str1);
     if (len2 < 0) len2 = strlen(str2);
 
-    GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
-                   (WCHAR *)&locale_cp, (sizeof(locale_cp)/sizeof(WCHAR)));
+    locale_cp = get_lcid_codepage(lcid);
 
     len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 128);
     if (len1W)
@@ -1403,19 +1578,18 @@
  */
 void LOCALE_Init(void)
 {
-    UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp = -1;
+    UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp = ~0U;
     LCID lcid = init_default_lcid( &unix_cp );
 
     NtSetDefaultLocale( FALSE, lcid );
     NtSetDefaultLocale( TRUE, lcid );
 
-    GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
-                    (LPWSTR)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR) );
+    ansi_cp = get_lcid_codepage(lcid);
     GetLocaleInfoW( lcid, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
     GetLocaleInfoW( lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
-    if (unix_cp == -1)
+    if (unix_cp == ~0U)
         GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
                     (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
 
@@ -1423,39 +1597,590 @@
     update_registry( lcid );
 }
 
+static HKEY WINAPI NLS_RegOpenKey(HKEY hRootKey, LPCWSTR szKeyName)
+{
+    UNICODE_STRING keyName;
+    OBJECT_ATTRIBUTES attr;
+    HKEY hkey;
+
+    RtlInitUnicodeString( &keyName, szKeyName );
+    InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
+
+    if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS)
+        hkey = 0;
+
+    return hkey;
+}
+
+static HKEY WINAPI NLS_RegOpenSubKey(HKEY hRootKey, LPCWSTR szKeyName)
+{
+    HKEY hKey = NLS_RegOpenKey(hRootKey, szKeyName);
+
+    if (hRootKey)
+        NtClose( hRootKey );
+
+    return hKey;
+}
+
+static BOOL WINAPI NLS_RegEnumSubKey(HKEY hKey, UINT ulIndex, LPWSTR szKeyName,
+                                     ULONG keyNameSize)
+{
+    BYTE buffer[80];
+    KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
+    DWORD dwLen;
+
+    if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
+                        sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
+        info->NameLength > keyNameSize)
+    {
+        return FALSE;
+    }
+
+    TRACE("info->Name %s info->NameLength %ld\n", debugstr_w(info->Name), info->NameLength);
+
+    memcpy( szKeyName, info->Name, info->NameLength);
+    szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
+
+    TRACE("returning %s\n", debugstr_w(szKeyName));
+    return TRUE;
+}
+
+static BOOL WINAPI NLS_RegEnumValue(HKEY hKey, UINT ulIndex,
+                                    LPWSTR szValueName, ULONG valueNameSize,
+                                    LPWSTR szValueData, ULONG valueDataSize)
+{
+    BYTE buffer[80];
+    KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
+    DWORD dwLen;
+
+    if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
+        buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
+        info->NameLength > valueNameSize ||
+        info->DataLength > valueDataSize)
+    {
+        return FALSE;
+    }
+
+    TRACE("info->Name %s info->DataLength %ld\n", debugstr_w(info->Name), info->DataLength);
+
+    memcpy( szValueName, info->Name, info->NameLength);
+    szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
+    memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
+    szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
+
+    TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
+    return TRUE;
+}
+
+static BOOL WINAPI NLS_RegGetDword(HKEY hKey, LPCWSTR szValueName, DWORD *lpVal)
+{
+    BYTE buffer[128];
+    const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
+    DWORD dwSize = sizeof(buffer);
+    UNICODE_STRING valueName;
+
+    RtlInitUnicodeString( &valueName, szValueName );
+
+    TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
+    if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
+                         buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
+        info->DataLength == sizeof(DWORD))
+    {
+        memcpy(lpVal, info->Data, sizeof(DWORD));
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static inline BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
+{
+    HMODULE hModule = GetModuleHandleW(kernel32W);
+    LANGID  langId;
+    LPCWSTR szResourceName = (LPCWSTR)(((lgrpid + 0x2000) >> 4) + 1);
+    HRSRC   hResource;
+    BOOL    bRet = FALSE;
+
+    /* FIXME: Is it correct to use the system default langid? */
+    langId = GetSystemDefaultLangID();
+
+    if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
+        langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
+
+    hResource = FindResourceExW( hModule, (LPWSTR)RT_STRING, szResourceName, langId );
+
+    if (hResource)
+    {
+        HGLOBAL hResDir = LoadResource( hModule, hResource );
+
+        if (hResDir)
+        {
+            ULONG   iResourceIndex = lgrpid & 0xf;
+            LPCWSTR lpResEntry = LockResource( hResDir );
+            ULONG   i;
+
+            for (i = 0; i < iResourceIndex; i++)
+                lpResEntry += *lpResEntry + 1;
+
+            if (*lpResEntry < nameSize)
+            {
+                memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
+                szName[*lpResEntry] = '\0';
+                bRet = TRUE;
+            }
+
+        }
+        FreeResource( hResource );
+    }
+    return bRet;
+}
+
+/* Registry keys for NLS related information */
+static const WCHAR szNlsKeyName[] = {
+    'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
+    'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+    'C','o','n','t','r','o','l','\\','N','l','s','\0'
+};
+
+static const WCHAR szLangGroupsKeyName[] = {
+    'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s','\0'
+};
+
+static const WCHAR szCountryListName[] = {
+    'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
+    'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
+    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+    'T','e','l','e','p','h','o','n','y','\\',
+    'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
+};
+
+
+/* Callback function ptrs for EnumSystemLanguageGroupsA/W */
+typedef struct
+{
+  LANGUAGEGROUP_ENUMPROCA procA;
+  LANGUAGEGROUP_ENUMPROCW procW;
+  DWORD    dwFlags;
+  LONG_PTR lParam;
+} ENUMLANGUAGEGROUP_CALLBACKS;
+
+/* Internal implementation of EnumSystemLanguageGroupsA/W */
+BOOL WINAPI NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
+{
+    WCHAR szNumber[10], szValue[4];
+    HKEY hKey;
+    BOOL bContinue = TRUE;
+    ULONG ulIndex = 0;
+
+    if (!lpProcs)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    switch (lpProcs->dwFlags)
+    {
+    case 0:
+        /* Default to LGRPID_INSTALLED */
+        lpProcs->dwFlags = LGRPID_INSTALLED;
+        /* Fall through... */
+    case LGRPID_INSTALLED:
+    case LGRPID_SUPPORTED:
+        break;
+    default:
+        SetLastError(ERROR_INVALID_FLAGS);
+        return FALSE;
+    }
+
+    hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
+
+    if (!hKey)
+      WARN("NLS registry key not found. Please apply the default registry file 'winedefault.reg'\n");
+
+    while (bContinue)
+    {
+        if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
+                              szValue, sizeof(szValue) ))
+        {
+            BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
+            LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
+
+            TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
+                   bInstalled ? "" : "not ");
+
+            if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
+            {
+                WCHAR szGrpName[48];
+
+                if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
+                    szGrpName[0] = '\0';
+
+                if (lpProcs->procW)
+                    bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
+                                                lpProcs->lParam );
+                else
+                {
+                    char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
+                    char szGrpNameA[48];
+
+                    /* FIXME: MSDN doesn't say which code page the W->A translation uses,
+                     *        or whether the language names are ever localised. Assume CP_ACP.
+                     */
+
+                    WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
+                    WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
+
+                    bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
+                                                lpProcs->lParam );
+                }
+            }
+
+            ulIndex++;
+        }
+        else
+            bContinue = FALSE;
+
+        if (!bContinue)
+            break;
+    }
+
+    if (hKey)
+        NtClose( hKey );
+
+    return TRUE;
+}
+
 /******************************************************************************
  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
+ *
+ * Call a users function for each language group available on the system.
+ *
+ * PARAMS
+ *  pLangGrpEnumProc [I] Callback function to call for each language group
+ *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
+ *  lParam           [I] User parameter to pass to pLangGrpEnumProc
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE. Use GetLastError() to determine the cause.
  */
-BOOL WINAPI EnumSystemLanguageGroupsA(
-  LANGUAGEGROUP_ENUMPROCA pLangGroupEnumProc, /* [in] callback function */
-  DWORD dwFlags,                              /* [in] language groups */
-  LONG_PTR  lParam                            /* [in] callback parameter */
-)
+BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
+                                      DWORD dwFlags, LONG_PTR lParam)
 {
-  FIXME("stub\n");
-  SetLastError( ERROR_INVALID_PARAMETER );
-  return FALSE;
+    ENUMLANGUAGEGROUP_CALLBACKS procs;
+
+    TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
+
+    procs.procA = pLangGrpEnumProc;
+    procs.procW = NULL;
+    procs.dwFlags = dwFlags;
+    procs.lParam = lParam;
+
+    return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
 }
 
 /******************************************************************************
  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
+ *
+ * See EnumSystemLanguageGroupsA.
  */
-BOOL WINAPI EnumSystemLanguageGroupsW(
-  LANGUAGEGROUP_ENUMPROCW pLangGroupEnumProc, /* [in] callback function */
-  DWORD dwFlags,                              /* [in] language groups */
-  LONG_PTR  lParam                            /* [in] callback parameter */
-)
+BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
+                                      DWORD dwFlags, LONG_PTR lParam)
 {
-  FIXME("stub\n");
-  SetLastError( ERROR_INVALID_PARAMETER );
-  return FALSE;
+    ENUMLANGUAGEGROUP_CALLBACKS procs;
+
+    TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
+
+    procs.procA = NULL;
+    procs.procW = pLangGrpEnumProc;
+    procs.dwFlags = dwFlags;
+    procs.lParam = lParam;
+
+    return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
+}
+
+/******************************************************************************
+ *           IsValidLanguageGroup    (KERNEL32.@)
+ *
+ * Determine if a language group is supported and/or installed.
+ *
+ * PARAMS
+ *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
+ *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
+ *
+ * RETURNS
+ *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
+ *  FALSE otherwise.
+ */
+BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
+{
+    static const WCHAR szFormat[] = { '%','x','\0' };
+    WCHAR szValueName[16], szValue[2];
+    BOOL bSupported = FALSE, bInstalled = FALSE;
+    HKEY hKey;
+
+
+    switch (dwFlags)
+    {
+    case LGRPID_INSTALLED:
+    case LGRPID_SUPPORTED:
+
+        hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
+
+        sprintfW( szValueName, szFormat, lgrpid );
+
+        if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)&szValue ))
+        {
+            bSupported = TRUE;
+
+            if (szValue[0] == '1')
+                bInstalled = TRUE;
+        }
+
+        if (hKey)
+            NtClose( hKey );
+
+        break;
+    }
+
+    if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
+        (dwFlags == LGRPID_INSTALLED && bInstalled))
+        return TRUE;
+
+    return FALSE;
+}
+
+/* Callback function ptrs for EnumLanguageGrouplocalesA/W */
+typedef struct
+{
+  LANGGROUPLOCALE_ENUMPROCA procA;
+  LANGGROUPLOCALE_ENUMPROCW procW;
+  DWORD    dwFlags;
+  LGRPID   lgrpid;
+  LONG_PTR lParam;
+} ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
+
+/* Internal implementation of EnumLanguageGrouplocalesA/W */
+BOOL WINAPI NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
+{
+    static const WCHAR szLocaleKeyName[] = {
+      'L','o','c','a','l','e','\0'
+    };
+    static const WCHAR szAlternateSortsKeyName[] = {
+      'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
+    };
+    WCHAR szNumber[10], szValue[4];
+    HKEY hKey;
+    BOOL bContinue = TRUE, bAlternate = FALSE;
+    LGRPID lgrpid;
+    ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
+
+    if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    if (lpProcs->dwFlags)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return FALSE;
+    }
+
+    hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLocaleKeyName );
+
+    if (!hKey)
+      WARN("NLS registry key not found. Please apply the default registry file 'winedefault.reg'\n");
+
+    while (bContinue)
+    {
+        if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
+                              szValue, sizeof(szValue) ))
+        {
+            lgrpid = strtoulW( szValue, NULL, 16 );
+
+            TRACE("lcid %s, grpid %ld (%smatched)\n", debugstr_w(szNumber),
+                   lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
+
+            if (lgrpid == lpProcs->lgrpid)
+            {
+                LCID lcid;
+
+                lcid = strtoulW( szNumber, NULL, 16 );
+
+                /* FIXME: native returns extra text for a few (17/150) locales, e.g:
+                 * '00000437          ;Georgian'
+                 * At present we only pass the LCID string.
+                 */
+
+                if (lpProcs->procW)
+                    bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
+                else
+                {
+                    char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
+
+                    WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
+
+                    bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
+                }
+            }
+
+            ulIndex++;
+        }
+        else
+        {
+            /* Finished enumerating this key */
+            if (!bAlternate)
+            {
+                /* Enumerate alternate sorts also */
+                hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
+                bAlternate = TRUE;
+                ulIndex = 0;
+            }
+            else
+                bContinue = FALSE; /* Finished both keys */
+        }
+
+        if (!bContinue)
+            break;
+    }
+
+    if (hKey)
+        NtClose( hKey );
+
+    return TRUE;
+}
+
+/******************************************************************************
+ *           EnumLanguageGroupLocalesA    (KERNEL32.@)
+ *
+ * Call a users function for every locale in a language group available on the system.
+ *
+ * PARAMS
+ *  pLangGrpLcEnumProc [I] Callback function to call for each locale
+ *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
+ *  dwFlags            [I] Reserved, set to 0
+ *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE. Use GetLastError() to determine the cause.
+ */
+BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
+                                      LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
+{
+    ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
+
+    TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
+
+    callbacks.procA   = pLangGrpLcEnumProc;
+    callbacks.procW   = NULL;
+    callbacks.dwFlags = dwFlags;
+    callbacks.lgrpid  = lgrpid;
+    callbacks.lParam  = lParam;
+
+    return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
+}
+
+/******************************************************************************
+ *           EnumLanguageGroupLocalesW    (KERNEL32.@)
+ *
+ * See EnumLanguageGroupLocalesA.
+ */
+BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
+                                      LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
+{
+    ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
+
+    TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
+
+    callbacks.procA   = NULL;
+    callbacks.procW   = pLangGrpLcEnumProc;
+    callbacks.dwFlags = dwFlags;
+    callbacks.lgrpid  = lgrpid;
+    callbacks.lParam  = lParam;
+
+    return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
+}
+
+/******************************************************************************
+ *           EnumSystemGeoID    (KERNEL32.@)
+ *
+ * Call a users function for every location available on the system.
+ *
+ * PARAMS
+ *  geoclass     [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
+ *  reserved     [I] Reserved, set to 0
+ *  pGeoEnumProc [I] Callback function to call for each location
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE. Use GetLastError() to determine the cause.
+ */
+BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
+{
+    static const WCHAR szCountryCodeValueName[] = {
+      'C','o','u','n','t','r','y','C','o','d','e','\0'
+    };
+    WCHAR szNumber[10];
+    HKEY hKey;
+    ULONG ulIndex = 0;
+
+    TRACE("(0x%08lX,0x%08lX,%p)\n", geoclass, reserved, pGeoEnumProc);
+
+    if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    hKey = NLS_RegOpenKey( 0, szCountryListName );
+
+    while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
+    {
+        BOOL bContinue = TRUE;
+        DWORD dwGeoId;
+        HKEY hSubKey = NLS_RegOpenKey( hKey, szNumber );
+
+        if (hSubKey)
+        {
+            if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
+            {
+                TRACE("Got geoid %ld\n", dwGeoId);
+
+                if (!pGeoEnumProc( dwGeoId ))
+                    bContinue = FALSE;
+            }
+
+            NtClose( hSubKey );
+        }
+
+        if (!bContinue)
+            break;
+
+        ulIndex++;
+    }
+
+    if (hKey)
+        NtClose( hKey );
+
+    return TRUE;
 }
 
 /******************************************************************************
  *           InvalidateNLSCache           (KERNEL32.@)
+ *
+ * Invalidate the cache of NLS values.
+ *
+ * PARAMS
+ *  None.
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE.
  */
 BOOL WINAPI InvalidateNLSCache(void)
 {
-  FIXME("stub\n");
+  FIXME("() stub\n");
   return FALSE;
 }
--- wine/dlls/kernel/kernel32.spec	2003-09-23 19:44:32.000000000 +0100
+++ wine-develop-old/dlls/kernel/kernel32.spec	2003-09-23 23:55:07.000000000 +0100
@@ -242,8 +242,8 @@
 @ stdcall EnumDateFormatsW(ptr long long)
 @ stub EnumDateFormatsExA
 @ stub EnumDateFormatsExW
-@ stub EnumLanguageGroupLocalesA
-@ stub EnumLanguageGroupLocalesW
+@ stdcall EnumLanguageGroupLocalesA(ptr long long ptr)
+@ stdcall EnumLanguageGroupLocalesW(ptr long long ptr)
 @ stdcall EnumResourceLanguagesA(long str str ptr long)
 @ stdcall EnumResourceLanguagesW(long wstr wstr ptr long)
 @ stdcall EnumResourceNamesA(long str ptr long)
@@ -252,7 +252,7 @@
 @ stdcall EnumResourceTypesW(long ptr long)
 @ stdcall EnumSystemCodePagesA(ptr long)
 @ stdcall EnumSystemCodePagesW(ptr long)
-@ stub EnumSystemGeoID
+@ stdcall EnumSystemGeoID(long long ptr)
 @ stdcall EnumSystemLanguageGroupsA(ptr long ptr)
 @ stdcall EnumSystemLanguageGroupsW(ptr long ptr)
 @ stdcall EnumSystemLocalesA(ptr long)
@@ -575,7 +575,7 @@
 @ stub IsSLCallback
 @ stdcall IsSystemResumeAutomatic()
 @ stdcall IsValidCodePage(long)
-@ stub IsValidLanguageGroup
+@ stdcall IsValidLanguageGroup(long long)
 @ stdcall IsValidLocale(long long)
 @ stdcall -register -i386 K32Thk1632Epilog()
 @ stdcall -register -i386 K32Thk1632Prolog()

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux