- Implementation of GetCPFileNameFromRegistry, GetNlsSectionName and IsValidCodePage.
authorFilip Navara <filip.navara@gmail.com>
Tue, 24 Aug 2004 17:21:12 +0000 (17:21 +0000)
committerFilip Navara <filip.navara@gmail.com>
Tue, 24 Aug 2004 17:21:12 +0000 (17:21 +0000)
- Partial implementation of MultiByteToWideChar and WideCharToMultiByte.
- Cleanup buffer allocation in some console functions.

svn path=/trunk/; revision=10668

reactos/lib/kernel32/kernel32.def
reactos/lib/kernel32/kernel32.edf
reactos/lib/kernel32/makefile
reactos/lib/kernel32/misc/console.c
reactos/lib/kernel32/misc/dllmain.c
reactos/lib/kernel32/misc/lang.c
reactos/lib/kernel32/misc/mbchars.c [deleted file]
reactos/lib/kernel32/misc/nls.c [new file with mode: 0755]

index bfaf164..2dbd0f5 100644 (file)
@@ -280,7 +280,7 @@ GetAtomNameW@12
 GetBinaryType@8
 GetBinaryTypeA@8
 GetBinaryTypeW@8
-;GetCPFileNameFromRegistry
+GetCPFileNameFromRegistry@12
 GetCPInfo@8
 GetCPInfoExA@12
 GetCPInfoExW@12
@@ -409,7 +409,7 @@ GetNamedPipeHandleStateW@28
 GetNamedPipeInfo@20
 GetNativeSystemInfo@4
 GetNextVDMCommand@4
-;GetNlsSectionName
+GetNlsSectionName@24
 GetNumaHighestNodeNumber@4
 GetNumaNodeProcessorMask@8
 ;GetNumaProcessorMap
index a33bd3b..a04690e 100644 (file)
@@ -1,4 +1,4 @@
-; $Id: kernel32.edf,v 1.32 2004/05/13 20:42:28 navaraf Exp $
+; $Id: kernel32.edf,v 1.33 2004/08/24 17:21:10 navaraf Exp $
 ;
 ; kernel32.edf
 ;
@@ -284,7 +284,7 @@ GetAtomNameW=GetAtomNameW@12
 GetBinaryType=GetBinaryTypeA@8
 GetBinaryTypeA=GetBinaryTypeA@8
 GetBinaryTypeW=GetBinaryTypeW@8
-;GetCPFileNameFromRegistry
+GetCPFileNameFromRegistry=GetCPFileNameFromRegistry@12
 GetCPInfo=GetCPInfo@8
 GetCPInfoExA=GetCPInfoExA@12
 GetCPInfoExW=GetCPInfoExW@12
@@ -414,7 +414,7 @@ GetNamedPipeHandleStateW=GetNamedPipeHandleStateW@28
 GetNamedPipeInfo=GetNamedPipeInfo@20
 GetNativeSystemInfo=GetNativeSystemInfo@4
 GetNextVDMCommand=GetNextVDMCommand@4
-;GetNlsSectionName
+GetNlsSectionName=GetNlsSectionName@24
 GetNumaHighestNodeNumber=GetNumaHighestNodeNumber@4
 GetNumaNodeProcessorMask=GetNumaNodeProcessorMask@8
 ;GetNumaProcessorMap
index 13febe8..e172e57 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: makefile,v 1.83 2004/08/01 23:27:55 navaraf Exp $
+# $Id: makefile,v 1.84 2004/08/24 17:21:10 navaraf Exp $
 
 PATH_TO_TOP = ../..
 
@@ -33,8 +33,9 @@ MISC_OBJECTS = misc/error.o misc/atom.o misc/handle.o misc/env.o \
                misc/console.o misc/time.o misc/timerqueue.o misc/toolhelp.o \
                misc/stubs.o misc/lang.o misc/ldr.o misc/res.o \
                misc/sysinfo.o misc/profile.o \
-               misc/mbchars.o misc/muldiv.o misc/computername.o \
+               misc/muldiv.o misc/nls.o misc/computername.o \
                misc/perfcnt.o misc/lzexpand_main.o misc/lcformat.o
+               
 
 FILE_OBJECTS = file/file.o file/curdir.o file/lfile.o file/dir.o \
                file/iocompl.o file/volume.o file/deviceio.o file/dosdev.o \
index 240b00a..573c4e0 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: console.c,v 1.77 2004/08/22 20:52:28 navaraf Exp $
+/* $Id: console.c,v 1.78 2004/08/24 17:21:11 navaraf Exp $
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
@@ -1073,10 +1073,10 @@ WriteConsoleA(HANDLE hConsoleOutput,
   USHORT Size;
   ULONG MessageSize;
 
-  Request = RtlAllocateHeap(GetProcessHeap(),
-                           HEAP_ZERO_MEMORY,
-                           sizeof(CSRSS_API_REQUEST) + 
-                           CSRSS_MAX_WRITE_CONSOLE_REQUEST);
+  Request = RtlAllocateHeap(GetProcessHeap(), 0,
+                           sizeof(CSRSS_API_REQUEST) +
+                           min(nNumberOfCharsToWrite,
+                           CSRSS_MAX_WRITE_CONSOLE_REQUEST));
   if (Request == NULL)
     {
       SetLastError(ERROR_OUTOFMEMORY);
@@ -1140,9 +1140,8 @@ BOOL STDCALL ReadConsoleA(HANDLE hConsoleInput,
    NTSTATUS Status;
    ULONG CharsRead = 0;
    
-   Reply = RtlAllocateHeap(GetProcessHeap(),
-                    HEAP_ZERO_MEMORY,
-                    sizeof(CSRSS_API_REPLY) + nNumberOfCharsToRead);
+   Reply = RtlAllocateHeap(GetProcessHeap(), 0,
+                           sizeof(CSRSS_API_REPLY) + nNumberOfCharsToRead);
    if (Reply == NULL)
      {
        SetLastError(ERROR_OUTOFMEMORY);
@@ -1946,9 +1945,9 @@ ReadConsoleOutputCharacterA(
   NTSTATUS Status;
   DWORD Size;
 
-  Reply = RtlAllocateHeap(GetProcessHeap(),
-                         HEAP_ZERO_MEMORY,
-                         sizeof(CSRSS_API_REPLY) + CSRSS_MAX_READ_CONSOLE_OUTPUT_CHAR);
+  Reply = RtlAllocateHeap(GetProcessHeap(), 0,
+                         sizeof(CSRSS_API_REPLY) +
+                         min(nLength, CSRSS_MAX_READ_CONSOLE_OUTPUT_CHAR));
   if (Reply == NULL)
     {
       SetLastError(ERROR_OUTOFMEMORY);
@@ -2035,9 +2034,9 @@ ReadConsoleOutputAttribute(
   NTSTATUS Status;
   DWORD Size, i;
   
-  Reply = RtlAllocateHeap(GetProcessHeap(),
-                         HEAP_ZERO_MEMORY,
-                         sizeof(CSRSS_API_REPLY) + CSRSS_MAX_READ_CONSOLE_OUTPUT_ATTRIB);
+  Reply = RtlAllocateHeap(GetProcessHeap(), 0,
+                         sizeof(CSRSS_API_REPLY) +
+                         min(nLength, CSRSS_MAX_READ_CONSOLE_OUTPUT_ATTRIB));
   if (Reply == NULL)
     {
       SetLastError(ERROR_OUTOFMEMORY);
@@ -2101,10 +2100,10 @@ WriteConsoleOutputCharacterA(HANDLE             hConsoleOutput,
   CSRSS_API_REPLY Reply;
   NTSTATUS Status;
   WORD Size;
-  
-  Request = RtlAllocateHeap(GetProcessHeap(),
-                           HEAP_ZERO_MEMORY,
-                           sizeof(CSRSS_API_REQUEST) + CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR);
+
+  Request = RtlAllocateHeap(GetProcessHeap(), 0,
+                           sizeof(CSRSS_API_REQUEST) +
+                           min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR));
   if( !Request )
     {
       SetLastError( ERROR_OUTOFMEMORY );
@@ -2156,9 +2155,9 @@ WriteConsoleOutputCharacterW(HANDLE               hConsoleOutput,
   NTSTATUS Status;
   WORD Size;
   
-  Request = RtlAllocateHeap(GetProcessHeap(),
-                           HEAP_ZERO_MEMORY,
-                           sizeof(CSRSS_API_REQUEST) + CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR);
+  Request = RtlAllocateHeap(GetProcessHeap(), 0,
+                           sizeof(CSRSS_API_REQUEST) +
+                           min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR));
   if( !Request )
     {
       SetLastError( ERROR_OUTOFMEMORY );
@@ -2223,9 +2222,9 @@ WriteConsoleOutputAttribute(
    WORD Size;
    int c;
 
-   Request = RtlAllocateHeap(GetProcessHeap(),
-                      HEAP_ZERO_MEMORY,
-                      sizeof(CSRSS_API_REQUEST) + CSRSS_MAX_WRITE_CONSOLE_OUTPUT_ATTRIB);
+   Request = RtlAllocateHeap(GetProcessHeap(), 0,
+                            sizeof(CSRSS_API_REQUEST) +
+                            min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_ATTRIB));
    if( !Request )
      {
        SetLastError( ERROR_OUTOFMEMORY );
@@ -3136,7 +3135,7 @@ GetConsoleCP( VOID )
 /*--------------------------------------------------------------
  *     SetConsoleCP
  *
- * @unimplemented
+ * @implemented
  */
 BOOL
 WINAPI
index afc8aff..f8e9d4c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: dllmain.c,v 1.35 2004/06/26 20:07:40 gdalsnes Exp $
+/* $Id: dllmain.c,v 1.36 2004/08/24 17:21:11 navaraf Exp $
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
@@ -40,6 +40,9 @@ CRITICAL_SECTION ConsoleLock;
 extern BOOL WINAPI DefaultConsoleCtrlHandler(DWORD Event);
 extern BOOL FASTCALL PROFILE_Init();
 
+extern BOOL FASTCALL NlsInit();
+extern VOID FASTCALL NlsUninit();
+
 /* FUNCTIONS *****************************************************************/
 
 static NTSTATUS
@@ -153,6 +156,12 @@ DllMain(HANDLE hDll,
             return FALSE;
           }
 
+       /* Initialize the National Language Support routines */
+        if (! NlsInit())
+          {
+            return FALSE;
+          }
+
        /* Initialize console ctrl handler */
        RtlInitializeCriticalSection(&ConsoleLock);
        SetConsoleCtrlHandler(DefaultConsoleCtrlHandler, TRUE);
@@ -168,6 +177,8 @@ DllMain(HANDLE hDll,
          {
            /* Insert more dll detach stuff here! */
 
+            NlsUninit();
+
            /* Delete DLL critical section */
            RtlDeleteCriticalSection (&ConsoleLock);
            RtlDeleteCriticalSection (&DllLock);
index edd70e4..8ac5920 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: lang.c,v 1.20 2004/06/26 20:10:50 gdalsnes Exp $
+/* $Id: lang.c,v 1.21 2004/08/24 17:21:11 navaraf Exp $
  *
  * COPYRIGHT: See COPYING in the top level directory
  * PROJECT  : ReactOS user mode libraries
@@ -907,20 +907,6 @@ GetUserGeoID(
 }
 
 
-/*
- * @unimplemented
- */
-BOOL
-STDCALL
-IsValidCodePage (
-    UINT    CodePage
-    )
-{
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-
 /*
  * @unimplemented
  */
diff --git a/reactos/lib/kernel32/misc/mbchars.c b/reactos/lib/kernel32/misc/mbchars.c
deleted file mode 100644 (file)
index 415abad..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-/* $Id: mbchars.c,v 1.5 2004/04/04 17:59:23 gvg Exp $
- *
- */
-#include <windows.h>
-
-
-/**********************************************************************
- * NAME                         PRIVATE
- *  IsInstalledCP@4
- *
- * RETURN VALUE
- *  TRUE if CodePage is installed in the system.
- */
-static
-BOOL
-STDCALL
-IsInstalledCP(UINT CodePage)
-{
-    /* FIXME */
-    return TRUE;
-}
-
-
-/**********************************************************************
- * NAME                         EXPORTED
- *  MultiByteToWideChar@24
- *
- * ARGUMENTS
- *  CodePage
- *      CP_ACP      ANSI code page
- *      CP_MACCP    Macintosh code page
- *      CP_OEMCP    OEM code page
- *      (UINT)      Any installed code page
- *
- *  dwFlags
- *      MB_PRECOMPOSED
- *      MB_COMPOSITE
- *      MB_ERR_INVALID_CHARS
- *      MB_USEGLYPHCHARS
- *
- *  lpMultiByteStr
- *      Input buffer;
- *
- *  cchMultiByte
- *      Size of MultiByteStr, or -1 if MultiByteStr is
- *      NULL terminated;
- *
- *  lpWideCharStr
- *      Output buffer;
- *
- *  cchWideChar
- *      Size (in WCHAR unit) of WideCharStr, or 0
- *      if the caller just wants to know how large
- *      WideCharStr should be for a successful
- *      conversion.
- *
- * RETURN VALUE
- *  0 on error; otherwise the number of WCHAR written
- *  in the WideCharStr buffer.
- *
- * NOTE
- *  A raw converter for now. It assumes lpMultiByteStr is
- *  NEVER multi-byte (that is each input character is
- *  8-bit ASCII) and is ALWAYS NULL terminated.
- *  FIXME-FIXME-FIXME-FIXME
- *
- * @implemented
- */
-INT
-STDCALL
-MultiByteToWideChar(
-    UINT    CodePage,
-    DWORD   dwFlags,
-    LPCSTR  lpMultiByteStr,
-    int     cchMultiByte,
-    LPWSTR  lpWideCharStr,
-    int     cchWideChar)
-{
-    int InStringLength = 0;
-    PCHAR   r;
-    PWCHAR  w;
-    int cchConverted;
-
-    /*
-     * Check the parameters.
-     */
-    if (/* --- CODE PAGE --- */
-        (  (CP_ACP != CodePage)
-                && (CP_MACCP != CodePage)
-                && (CP_OEMCP != CodePage)
-                && (FALSE == IsInstalledCP(CodePage)) )
-        /* --- FLAGS --- */
-        || (dwFlags & ~(MB_PRECOMPOSED | MB_COMPOSITE |
-                        MB_ERR_INVALID_CHARS | MB_USEGLYPHCHARS))
-        /* --- INPUT BUFFER --- */
-        || (NULL == lpMultiByteStr)  )
-    {
-        SetLastError (ERROR_INVALID_PARAMETER);
-        return 0;
-    }
-    /*
-     * Compute the input buffer length.
-     */
-    //if (-1 == cchMultiByte)
-    if (cchMultiByte < 0)
-    {
-        InStringLength = lstrlenA(lpMultiByteStr) + 1;
-    }
-    else
-    {
-        InStringLength = cchMultiByte;
-    }
-    /*
-     * Does caller query for output
-     * buffer size?
-     */
-    if (0 == cchWideChar)
-    {
-        //SetLastError(ERROR_SUCCESS); /* according to wine tests this shouldn't be touched on success.
-        return InStringLength;
-    }
-    /*
-     * Is space provided for the translated
-     * string enough?
-     */
-    if (cchWideChar < InStringLength)
-    {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return 0;
-    }
-    /*
-     * Raw 8- to 16-bit conversion.
-     */
-    for (cchConverted = 0,
-        r = (PCHAR)lpMultiByteStr,
-        w = (PWCHAR)lpWideCharStr;
-
-        cchConverted < InStringLength;
-
-        r++,
-        w++,
-        cchConverted++)
-    {
-        *w = (WCHAR)(unsigned char) *r;
-    }
-    /*
-     * Return how many characters we
-     * wrote in the output buffer.
-     */
-    //SetLastError(ERROR_SUCCESS); /* according to wine tests this shouldn't be touched on success.
-    return cchConverted;
-}
-
-
-/**********************************************************************
- * NAME                         EXPORTED
- *  WideCharToMultiByte@32
- * 
- *  Not yet implemented complete (without NLS so far)
- *
- * ARGUMENTS
- *  CodePage
- *      CP_ACP ANSI code page 
- *      CP_MACCP Macintosh code page 
- *      CP_OEMCP OEM code page 
- *      CP_SYMBOL Symbol code page (42) 
- *      CP_THREAD_ACP Current thread's ANSI code page 
- *      CP_UTF7 Translate using UTF-7 
- *      CP_UTF8 Translate using UTF-8 
- *      (UINT)      Any installed code page
- *
- *  dwFlags
- *      WC_NO_BEST_FIT_CHARS    
- *      WC_COMPOSITECHECK Convert composite characters to precomposed characters. 
- *      WC_DISCARDNS Discard nonspacing characters during conversion. 
- *      WC_SEPCHARS Generate separate characters during conversion. This is the default conversion behavior. 
- *      WC_DEFAULTCHAR Replace exceptions with the default character during conversion. 
- *
- *  lpWideCharStr 
- *      Points to the wide-character string to be converted. 
- *
- *  cchWideChar
- *      Size (in WCHAR unit) of WideCharStr, or 0
- *      if the caller just wants to know how large
- *      WideCharStr should be for a successful
- *      conversion.
- *  lpMultiByteStr 
- *      Points to the buffer to receive the translated string. 
- *  cchMultiByte 
- *      Specifies the size in bytes of the buffer pointed to by the 
- *      lpMultiByteStr parameter. If this value is zero, the function 
- *      returns the number of bytes required for the buffer. 
- *  lpDefaultChar 
- *      Points to the character used if a wide character cannot be 
- *      represented in the specified code page. If this parameter is 
- *      NULL, a system default value is used. 
-        FIXME: ignored
- *  lpUsedDefaultChar 
- *      Points to a flag that indicates whether a default character was used. 
- *      This parameter may be NULL. 
-        FIXME: allways set to FALSE.
- *
- *
- *
- * RETURN VALUE
- *  0 on error; otherwise the number of bytes written
- *  in the lpMultiByteStr buffer. Or the number of
- *  bytes needed for the lpMultiByteStr buffer if cchMultiByte is zero.
- *
- * NOTE
- *  A raw converter for now. It just cuts off the upper 9 Bit.
- *  So the MBCS-string does not contain any LeadCharacters
- *  FIXME - FIXME - FIXME - FIXME
- *
- * @implemented
- */
-int
-STDCALL
-WideCharToMultiByte(
-    UINT    CodePage,
-    DWORD   dwFlags,
-    LPCWSTR lpWideCharStr,
-    int     cchWideChar,
-    LPSTR   lpMultiByteStr,
-    int     cchMultiByte,
-    LPCSTR  lpDefaultChar,
-    LPBOOL  lpUsedDefaultChar
-    )
-{
-    int wi, di;  // wide counter, dbcs byte count
-
-    /*
-     * Check the parameters.
-     */
-    if (    /* --- CODE PAGE --- */
-        (   (CP_ACP != CodePage)
-            && (CP_MACCP != CodePage)
-            && (CP_OEMCP != CodePage)
-            && (CP_SYMBOL != CodePage)
-            && (CP_THREAD_ACP != CodePage)
-            && (CP_UTF7 != CodePage)
-            && (CP_UTF8 != CodePage)
-            && (FALSE == IsInstalledCP (CodePage))
-            )
-        /* --- FLAGS --- */
-        || (dwFlags & ~(/*WC_NO_BEST_FIT_CHARS
-                |*/ WC_COMPOSITECHECK
-                | WC_DISCARDNS
-                | WC_SEPCHARS
-                | WC_DEFAULTCHAR
-                )
-            )
-        /* --- INPUT BUFFER --- */
-        || (NULL == lpWideCharStr)
-        )
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return 0;
-    }
-
-    // for now, make no difference but only convert cut the characters to 7Bit
-    //if (cchWideChar == -1) // assume its a 0-terminated str
-    if (cchWideChar < 0) // assume its a 0-terminated str
-    {           // and determine its length
-//        for (cchWideChar=0; lpWideCharStr[cchWideChar]!=0; cchWideChar++)
-//            cchWideChar++;
-        for (cchWideChar = 0; lpWideCharStr[cchWideChar] != 0; cchWideChar++) {
-        }
-        cchWideChar++; // length includes the null terminator
-    }
-
-    // user wants to determine needed space
-    if (cchMultiByte == 0) 
-    {
-        //SetLastError(ERROR_SUCCESS); /* according to wine tests this shouldn't be touched on success.
-        return cchWideChar;         // FIXME: determine correct.
-    }
-    // the lpWideCharStr is cchWideChar characters long.
-    for (wi=0, di=0; wi<cchWideChar && di<cchMultiByte; ++wi, ++di)
-    {
-        // Flag and a not displayable char    FIXME
-        /*if( (dwFlags&WC_NO_BEST_FIT_CHARS) && (lpWideCharStr[wi] >127) ) 
-        {
-            lpMultiByteStr[di]=
-            *lpUsedDefaultChar = TRUE;
-
-        }*/
-        // FIXME
-        // just cut off the upper 9 Bit, since vals>=128 mean LeadByte.
-        lpMultiByteStr[di] = lpWideCharStr[wi] & 0x007F;
-    }
-    // has MultiByte exceeded but Wide is still in the string?
-    if (wi < cchWideChar && di >= cchMultiByte)
-    {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return 0;
-    }
-    // else return # of bytes wirtten to MBCSbuffer (di)
-    //SetLastError(ERROR_SUCCESS); /* according to wine tests this shouldn't be touched on success.
-    // FIXME: move that elsewhere
-    if (lpUsedDefaultChar != NULL) *lpUsedDefaultChar = FALSE; 
-    return di;
-}
-
-/* EOF */
diff --git a/reactos/lib/kernel32/misc/nls.c b/reactos/lib/kernel32/misc/nls.c
new file mode 100755 (executable)
index 0000000..5adca77
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * ReactOS Kernel
+ * Copyright (C) 2004 ReactOS Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/**
+ * @brief National Language Support.
+ * @author Filip Navara
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <k32.h>
+#define NDEBUG
+#include "../include/debug.h"
+
+/* GLOBAL VARIABLES ***********************************************************/
+
+typedef struct _CODEPAGE_ENTRY
+{
+   LIST_ENTRY Entry;
+   UINT CodePage;
+   HANDLE SectionHandle;
+   PBYTE SectionMapping;
+   CPTABLEINFO CodePageTable;
+} CODEPAGE_ENTRY, *PCODEPAGE_ENTRY;
+
+/* Sequence length based on the first character. */
+static const char UTF8Length[128] =
+{
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
+   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xC0 - 0xCF */
+   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xD0 - 0xDF */
+   2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xE0 - 0xEF */
+   3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 0, 0  /* 0xF0 - 0xFF */
+};
+
+/* First byte mask depending on UTF-8 sequence length. */
+static const unsigned char UTF8Mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+/* FIXME: Change to HASH table or linear array. */
+static LIST_ENTRY CodePageListHead;
+static CODEPAGE_ENTRY AnsiCodePage;
+static CODEPAGE_ENTRY OemCodePage;
+static CRITICAL_SECTION CodePageListLock;
+
+/* FORWARD DECLARATIONS *******************************************************/
+
+BOOL STDCALL
+GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown,
+                  LPSTR BaseName, LPSTR Result, ULONG ResultSize);
+
+BOOL STDCALL
+GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize);
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+/**
+ * Internal NLS related stuff initialization.
+ */
+BOOL FASTCALL
+NlsInit()
+{
+   UNICODE_STRING DirName;
+   OBJECT_ATTRIBUTES ObjectAttributes;
+   HANDLE Handle;
+
+   InitializeListHead(&CodePageListHead);
+   RtlInitializeCriticalSection(&CodePageListLock);
+
+   /*
+    * FIXME: Eventually this should be done only for the NLS Server
+    * process, but since we don't have anything like that (yet?) we
+    * always try to create the "\Nls" directory here.
+    */
+   RtlInitUnicodeString(&DirName, L"\\Nls");
+   InitializeObjectAttributes(&ObjectAttributes, &DirName,
+                              OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
+                              NULL, NULL);
+   NtCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes);
+   NtClose(Handle);
+   
+   /* Setup ANSI code page. */
+   AnsiCodePage.CodePage = CP_ACP;
+   AnsiCodePage.SectionHandle = NULL;
+   AnsiCodePage.SectionMapping = NtCurrentTeb()->Peb->AnsiCodePageData;
+   RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping,
+                        &AnsiCodePage.CodePageTable);
+   InsertTailList(&CodePageListHead, &AnsiCodePage.Entry);
+
+   /* Setup OEM code page. */
+   OemCodePage.CodePage = CP_OEMCP;
+   OemCodePage.SectionHandle = NULL;
+   OemCodePage.SectionMapping = NtCurrentTeb()->Peb->OemCodePageData;
+   RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping,
+                        &OemCodePage.CodePageTable);
+   InsertTailList(&CodePageListHead, &OemCodePage.Entry);
+
+   return TRUE;
+}
+
+/**
+ * Internal NLS related stuff uninitialization.
+ */
+VOID FASTCALL
+NlsUninit()
+{
+   PCODEPAGE_ENTRY Current;
+
+   /* Delete the code page list. */
+   while (!IsListEmpty(&CodePageListHead))
+   {
+      Current = CONTAINING_RECORD(CodePageListHead.Flink, CODEPAGE_ENTRY, Entry);
+      if (Current->SectionHandle != NULL)
+      {
+         UnmapViewOfFile(Current->SectionMapping);
+         NtClose(Current->SectionHandle);
+      }
+      RemoveHeadList(&CodePageListHead);
+   }
+   RtlDeleteCriticalSection(&CodePageListLock);
+}
+
+/**
+ * Internal function to get structure containing a code page information
+ * of code page that is already loaded.
+ *
+ * @param CodePage
+ *        Number of the code page. Special values like CP_OEMCP, CP_ACP
+ *        or CP_UTF8 aren't allowed.
+ *
+ * @return Code page entry or NULL if the specified code page hasn't
+ *         been loaded yet.
+ */
+PCODEPAGE_ENTRY FASTCALL
+IntGetLoadedCodePageEntry(UINT CodePage)
+{
+   LIST_ENTRY *CurrentEntry;
+   PCODEPAGE_ENTRY Current;
+
+   RtlEnterCriticalSection(&CodePageListLock);
+   for (CurrentEntry = CodePageListHead.Flink;
+        CurrentEntry != &CodePageListHead;
+        CurrentEntry = CurrentEntry->Flink)
+   {
+      Current = CONTAINING_RECORD(CurrentEntry, CODEPAGE_ENTRY, Entry);
+      if (Current->CodePage == CodePage)
+      {
+         RtlLeaveCriticalSection(&CodePageListLock);
+         return Current;
+      }
+   }
+   RtlLeaveCriticalSection(&CodePageListLock);
+
+   return NULL;
+}
+
+/**
+ * Internal function to get structure containing a code page information.
+ *
+ * @param CodePage
+ *        Number of the code page. Special values like CP_OEMCP, CP_ACP
+ *        or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't.
+ *
+ * @return Code page entry.
+ */
+PCODEPAGE_ENTRY FASTCALL
+IntGetCodePageEntry(UINT CodePage)
+{
+   CHAR SectionName[40];
+   NTSTATUS Status;
+   HANDLE SectionHandle = INVALID_HANDLE_VALUE, FileHandle;
+   PBYTE SectionMapping;
+   OBJECT_ATTRIBUTES ObjectAttributes;
+   ANSI_STRING AnsiName;
+   UNICODE_STRING UnicodeName;
+   WCHAR FileName[MAX_PATH + 1];
+   UINT FileNamePos;
+   PCODEPAGE_ENTRY CodePageEntry;
+   
+   if (CodePage == CP_THREAD_ACP)
+   {
+      if (!GetLocaleInfoW(GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE | 
+                          LOCALE_RETURN_NUMBER, (WCHAR *)&CodePage,
+                          sizeof(CodePage) / sizeof(WCHAR)))
+      {
+         /* Last error is set by GetLocaleInfoW. */
+         return 0;
+      }
+   }
+   else if (CodePage == CP_MACCP)
+   {
+      if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | 
+                          LOCALE_RETURN_NUMBER, (WCHAR *)&CodePage,
+                          sizeof(CodePage) / sizeof(WCHAR)))
+      {
+         /* Last error is set by GetLocaleInfoW. */
+         return 0;
+      }
+   }
+
+   /* Try searching for loaded page first. */
+   CodePageEntry = IntGetLoadedCodePageEntry(CodePage);
+   if (CodePageEntry != NULL)
+   {
+      return CodePageEntry;
+   }
+
+   /*
+    * Yes, we really want to lock here. Otherwise it can happen that
+    * two parallel requests will try to get the entry for the same
+    * code page and we would load it twice.
+    */
+   RtlEnterCriticalSection(&CodePageListLock);
+
+   /* Generate the section name. */
+   if (!GetNlsSectionName(CodePage, 10, 0, "\\Nls\\NlsSectionCP",
+                          SectionName, sizeof(SectionName)))
+   {
+      RtlLeaveCriticalSection(&CodePageListLock);
+      return NULL;
+   }
+   RtlInitAnsiString(&AnsiName, SectionName);
+   RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE);
+   InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0,
+                              NULL, NULL);
+
+   /* Try to open the section first */
+   Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes);
+
+   /* If the section doesn't exist, try to create it. */
+   if (Status == STATUS_UNSUCCESSFUL ||
+       Status == STATUS_OBJECT_NAME_NOT_FOUND ||
+       Status == STATUS_OBJECT_PATH_NOT_FOUND)
+   {
+      FileNamePos = GetSystemDirectoryW(FileName, MAX_PATH);
+      if (GetCPFileNameFromRegistry(CodePage, FileName + FileNamePos + 1,
+                                    MAX_PATH - FileNamePos - 1))
+      {
+         FileName[FileNamePos] = L'\\';
+         FileName[MAX_PATH] = 0;
+         FileHandle = CreateFileW(FileName, FILE_GENERIC_READ, FILE_SHARE_READ,
+                                  NULL, OPEN_EXISTING, 0, NULL);
+         Status = NtCreateSection(&SectionHandle, SECTION_MAP_READ,
+                                  &ObjectAttributes, NULL, PAGE_READONLY,
+                                  SEC_FILE, FileHandle);
+      }
+   }
+   RtlFreeUnicodeString(&UnicodeName);
+
+   if (!NT_SUCCESS(Status))
+   {
+      RtlLeaveCriticalSection(&CodePageListLock);
+      return NULL;
+   }
+
+   SectionMapping = MapViewOfFile(SectionHandle, FILE_MAP_READ, 0, 0, 0);
+   if (SectionMapping == NULL)
+   {
+      NtClose(SectionHandle);
+      RtlLeaveCriticalSection(&CodePageListLock);
+      return NULL;
+   }
+
+   CodePageEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY));
+   if (CodePageEntry == NULL)
+   {
+      NtClose(SectionHandle);
+      RtlLeaveCriticalSection(&CodePageListLock);
+      return NULL;
+   }
+
+   CodePageEntry->CodePage = CodePage;
+   CodePageEntry->SectionHandle = SectionHandle;
+   CodePageEntry->SectionMapping = SectionMapping;
+   RtlInitCodePageTable((PUSHORT)SectionMapping, &CodePageEntry->CodePageTable);
+   
+   /* Insert the new entry to list and unlock. Uff. */
+   InsertTailList(&CodePageListHead, &CodePageEntry->Entry);
+   RtlLeaveCriticalSection(&CodePageListLock);
+
+   return CodePageEntry;
+}
+
+/**
+ * Internal version of MultiByteToWideChar for UTF8.
+ *
+ * @see MultiByteToWideChar
+ * @todo Add UTF8 validity checks.
+ */
+INT STDCALL
+IntMultiByteToWideCharUTF8(DWORD Flags,
+                           LPCSTR MultiByteString, INT MultiByteCount,
+                           LPWSTR WideCharString, INT WideCharCount)
+{
+   LPCSTR MbsEnd;
+   UCHAR Char, Length;
+   WCHAR WideChar;
+   ULONG Count;
+   
+   if (Flags != 0)
+   {
+      SetLastError(ERROR_INVALID_FLAGS);
+      return 0;
+   }
+
+   /* Does caller query for output buffer size? */
+   if (WideCharCount == 0)
+   {
+      MbsEnd = MultiByteString + MultiByteCount;
+      for (; MultiByteString < MbsEnd; WideCharCount++)
+      {
+         Char = *MultiByteString++;
+         if (Char < 0xC0)
+            continue;
+         MultiByteString += UTF8Length[Char - 0x80];
+      }
+      return WideCharCount;
+   }
+
+   MbsEnd = MultiByteString + MultiByteCount;
+   for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
+   {
+      Char = *MultiByteString++;
+      if (Char < 0x80)
+      {
+         *WideCharString++ = Char;
+         continue;
+      }
+      Length = UTF8Length[Char - 0x80];
+      WideChar = UTF8Mask[Length];
+      while (Length && MultiByteString < MbsEnd)
+         WideChar = (WideChar << 6) | *MultiByteString++;
+      *WideCharString++ = WideChar;
+   }
+
+   if (MultiByteString < MbsEnd)
+      SetLastError(ERROR_INSUFFICIENT_BUFFER);
+
+   return Count;
+}
+
+/**
+ * Internal version of MultiByteToWideChar for code page tables.
+ *
+ * @see MultiByteToWideChar
+ * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
+ *       DBCS codepages.
+ */
+INT STDCALL
+IntMultiByteToWideCharCP(UINT CodePage, DWORD Flags,
+                         LPCSTR MultiByteString, INT MultiByteCount,
+                         LPWSTR WideCharString, INT WideCharCount)
+{
+   PCODEPAGE_ENTRY CodePageEntry;
+   PCPTABLEINFO CodePageTable;
+   LPCSTR TempString;
+   INT TempLength;
+
+   /* Get code page table. */
+   CodePageEntry = IntGetCodePageEntry(CodePage);
+   if (CodePageEntry == NULL)
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return 0;
+   }
+   CodePageTable = &CodePageEntry->CodePageTable;
+
+   /* Different handling for DBCS code pages. */
+   if (CodePageTable->MaximumCharacterSize > 1)
+   {
+      /* UNIMPLEMENTED */
+      return 0;
+   }
+   else
+   {
+      /* Check for invalid characters. */
+      if (Flags & MB_ERR_INVALID_CHARS)
+      {
+         for (TempString = MultiByteString, TempLength = MultiByteCount;
+              TempLength > 0;
+              TempString++, TempLength--)
+         {
+            if (CodePageTable->MultiByteTable[(UCHAR)*TempString] ==
+                CodePageTable->UniDefaultChar &&
+                *TempString != CodePageEntry->CodePageTable.DefaultChar)
+            {
+               SetLastError(ERROR_NO_UNICODE_TRANSLATION);
+               return 0;
+            }
+         }
+      }
+
+      /* Does caller query for output buffer size? */
+      if (WideCharCount == 0)
+         return MultiByteCount;
+
+      /* Adjust buffer size. Wine trick ;-) */
+      if (WideCharCount < MultiByteCount)
+      {
+         MultiByteCount = WideCharCount;
+         SetLastError(ERROR_INSUFFICIENT_BUFFER);
+      }
+
+      for (TempLength = MultiByteCount;
+           TempLength > 0;
+           MultiByteString++, TempLength--)
+      {
+         *WideCharString++ = CodePageTable->MultiByteTable[(UCHAR)*MultiByteString];
+      }
+
+      return MultiByteCount;
+   }
+}
+
+/**
+ * Internal version of WideCharToMultiByte for UTF8.
+ *
+ * @see WideCharToMultiByte
+ */
+INT STDCALL
+IntWideCharToMultiByteUTF8(UINT CodePage, DWORD Flags,
+                           LPCWSTR WideCharString, INT WideCharCount,
+                           LPSTR MultiByteString, INT MultiByteCount,
+                           LPCSTR DefaultChar, LPBOOL UsedDefaultChar)
+{
+   DPRINT1("WideCharToMultiByte for CP_UTF8 is not implemented!\n");
+   return 0;
+}
+
+/**
+ * Internal version of WideCharToMultiByte for code page tables.
+ *
+ * @see WideCharToMultiByte
+ * @todo Handle default characters and flags.
+ */
+INT STDCALL
+IntWideCharToMultiByteCP(UINT CodePage, DWORD Flags,
+                         LPCWSTR WideCharString, INT WideCharCount,
+                         LPSTR MultiByteString, INT MultiByteCount,
+                         LPCSTR DefaultChar, LPBOOL UsedDefaultChar)
+{
+   PCODEPAGE_ENTRY CodePageEntry;
+   PCPTABLEINFO CodePageTable;
+   INT TempLength;
+
+   /* Get code page table. */
+   CodePageEntry = IntGetCodePageEntry(CodePage);
+   if (CodePageEntry == NULL)
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return 0;
+   }
+   CodePageTable = &CodePageEntry->CodePageTable;
+
+   /* Different handling for DBCS code pages. */
+   if (CodePageTable->MaximumCharacterSize > 1)
+   {
+      DPRINT1("WideCharToMultiByte for DBCS codepages is not implemented!\n");
+      return 0;
+   }
+   else
+   {
+      /* Does caller query for output buffer size? */
+      if (MultiByteCount == 0)
+         return WideCharCount;
+
+      /* Adjust buffer size. Wine trick ;-) */
+      if (MultiByteCount < WideCharCount)
+      {
+         WideCharCount = MultiByteCount;
+         SetLastError(ERROR_INSUFFICIENT_BUFFER);
+      }
+
+      for (TempLength = WideCharCount;
+           TempLength > 0;
+           WideCharString++, TempLength--)
+      {
+         *MultiByteString++ = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
+      }
+
+      /* FIXME */
+      if (UsedDefaultChar != NULL)
+         *UsedDefaultChar = FALSE;
+
+      return WideCharCount;
+   }
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/**
+ * Construct a name of NLS section.
+ *
+ * @param CodePage
+ *        Code page number.
+ * @param Base
+ *        Integer base used for converting to string. Usually set to 10.
+ * @param Unknown
+ *        As the name suggests the meaning of this parameter is unknown.
+ *        The native version of Kernel32 passes it as the third parameter
+ *        to NlsConvertIntegerToString function, which is used for the
+ *        actual conversion of the code page number.
+ * @param BaseName
+ *        Base name of the section. (ex. "\\Nls\\NlsSectionCP")
+ * @param Result
+ *        Buffer that will hold the constructed name.
+ * @param ResultSize
+ *        Size of the buffer for the result.
+ *
+ * @return TRUE if the buffer was large enough and was filled with
+ *         the requested information, FALSE otherwise.
+ *
+ * @implemented
+ */
+BOOL STDCALL
+GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown,
+                  LPSTR BaseName, LPSTR Result, ULONG ResultSize)
+{
+   CHAR Integer[11];
+
+   if (!NT_SUCCESS(RtlIntegerToChar(CodePage, Base, sizeof(Integer), Integer)))
+      return FALSE;
+
+   /*
+    * If the name including the terminating NULL character doesn't 
+    * fit in the output buffer then fail.
+    */
+   if (strlen(Integer) + strlen(BaseName) >= ResultSize)
+      return FALSE;
+
+   lstrcpyA(Result, BaseName);
+   lstrcatA(Result, Integer);
+
+   return TRUE;
+}
+
+/**
+ * Get file name of code page definition file.
+ *
+ * @param CodePage
+ *        Code page number to get file name of.
+ * @param FileName
+ *        Buffer that is filled with file name of successful return. Can 
+ *        be set to NULL.
+ * @param FileNameSize
+ *        Size of the buffer to hold file name in WCHARs.
+ *
+ * @return TRUE if the file name was retrieved, FALSE otherwise.
+ *
+ * @implemented
+ */
+BOOL STDCALL
+GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize)
+{
+   WCHAR ValueNameBuffer[11];
+   UNICODE_STRING KeyName, ValueName;
+   OBJECT_ATTRIBUTES ObjectAttributes;
+   NTSTATUS Status;
+   HANDLE KeyHandle;
+   PKEY_VALUE_PARTIAL_INFORMATION Kvpi;
+   DWORD KvpiSize;
+
+   /* Convert the codepage number to string. */
+   ValueName.Buffer = ValueNameBuffer;
+   ValueName.MaximumLength = sizeof(ValueNameBuffer);
+   if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName)))
+      return FALSE;
+   
+   /* Open the registry key containing file name mappings. */
+   RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\"
+                        L"CurrentControlSet\\Control\\Nls\\CodePage");
+   InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE,
+                             NULL, NULL);
+   Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
+   if (!NT_SUCCESS(Status))
+   {
+      RtlFreeUnicodeString(&ValueName);
+      return FALSE;
+   }
+
+   /* Allocate buffer that will be used to query the value data. */
+   KvpiSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
+              (MAX_PATH * sizeof(WCHAR));
+   Kvpi = HeapAlloc(GetProcessHeap(), 0, KvpiSize);
+   if (Kvpi == NULL)
+   {
+      NtClose(KeyHandle);
+      RtlFreeUnicodeString(&ValueName);
+      return FALSE;
+   }
+
+   /* Query the file name for our code page. */
+   Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation,
+                            Kvpi, KvpiSize, &KvpiSize);
+   RtlFreeUnicodeString(&ValueName);
+   NtClose(KeyHandle);
+
+   /* Check if we succeded and the value is non-empty string. */
+   if (NT_SUCCESS(Status) && Kvpi->Type == REG_SZ &&
+       Kvpi->DataLength > sizeof(WCHAR))
+   {
+      if (FileName != NULL)
+      {
+         lstrcpynW(FileName, (WCHAR*)Kvpi->Data,
+                   min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize));
+      }
+      return TRUE;
+   }
+
+   return FALSE;
+}
+
+/**
+ * Detect if specified code page is valid and present in the system.
+ *
+ * @param CodePage
+ *        Code page number to query.
+ *
+ * @return TRUE if code page is present.
+ */
+BOOL STDCALL
+IsValidCodePage(UINT CodePage)
+{
+   if (CodePage == CP_UTF8 || CodePage == CP_UTF7)
+      return TRUE;
+   if (IntGetLoadedCodePageEntry(CodePage))
+      return TRUE;
+   return GetCPFileNameFromRegistry(CodePage, NULL, 0);
+}
+
+/**
+ * Convert a multi-byte string to wide-charater equivalent.
+ *
+ * @param CodePage
+ *        Code page to be used to perform the conversion. It can be also
+ *        one of the special values (CP_ACP for ANSI code page, CP_MACCP
+ *        for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
+ *        for thread active code page, CP_UTF7 or CP_UTF8).
+ * @param Flags
+ *        Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
+ *        MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
+ * @param MultiByteString
+ *        Input buffer.
+ * @param MultiByteCount
+ *        Size of MultiByteString, or -1 if MultiByteString is NULL
+ *        terminated.
+ * @param WideCharString
+ *        Output buffer.
+ * @param WideCharCount
+ *        Size in WCHARs of WideCharString, or 0 if the caller just wants
+ *        to know how large WideCharString should be for a successful
+ *        conversion.
+ *
+ * @return Zero on error, otherwise the number of WCHARs written
+ *         in the WideCharString buffer.
+ *
+ * @implemented
+ */
+INT STDCALL
+MultiByteToWideChar(UINT CodePage, DWORD Flags,
+                    LPCSTR MultiByteString, INT MultiByteCount,
+                    LPWSTR WideCharString, INT WideCharCount)
+{
+   /* Check the parameters. */
+   if (MultiByteString == NULL ||
+       (WideCharString == NULL && WideCharCount > 0))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return 0;
+   }
+
+   /* Determine the input string length. */
+   if (MultiByteCount < 0)
+   {
+      MultiByteCount = lstrlenA(MultiByteString) + 1;
+   }
+
+   switch (CodePage)
+   {
+      case CP_UTF8:
+         return IntMultiByteToWideCharUTF8(
+            Flags, MultiByteString, MultiByteCount,
+            WideCharString, WideCharCount);
+
+      case CP_UTF7:
+         DPRINT1("MultiByteToWideChar for CP_UTF7 is not implemented!\n");
+         return 0;
+
+      case CP_SYMBOL:
+         DPRINT1("MultiByteToWideChar for CP_SYMBOL is not implemented!\n");
+         return 0;
+
+      default:
+         return IntMultiByteToWideCharCP(
+            CodePage, Flags, MultiByteString, MultiByteCount,
+            WideCharString, WideCharCount);
+   }
+}
+
+/**
+ * Convert a wide-charater string to closest multi-byte equivalent.
+ * 
+ * @param CodePage
+ *        Code page to be used to perform the conversion. It can be also
+ *        one of the special values (CP_ACP for ANSI code page, CP_MACCP
+ *        for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
+ *        for thread active code page, CP_UTF7 or CP_UTF8).
+ * @param Flags
+ *        Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
+ *        WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
+ * @param WideCharString
+ *        Points to the wide-character string to be converted. 
+ * @param WideCharCount
+ *        Size in WCHARs of WideCharStr, or 0 if the caller just wants to
+ *        know how large WideCharString should be for a successful conversion.
+ * @param MultiByteString
+ *        Points to the buffer to receive the translated string. 
+ * @param MultiByteCount
+ *        Specifies the size in bytes of the buffer pointed to by the 
+ *        MultiByteString parameter. If this value is zero, the function 
+ *        returns the number of bytes required for the buffer. 
+ * @param DefaultChar 
+ *        Points to the character used if a wide character cannot be 
+ *        represented in the specified code page. If this parameter is 
+ *        NULL, a system default value is used. 
+ * @param UsedDefaultChar 
+ *        Points to a flag that indicates whether a default character was
+ *        used. This parameter can be NULL. 
+ *
+ * @return Zero on error, otherwise the number of bytes written in the
+ *         MultiByteString buffer. Or the number of bytes needed for
+ *         the MultiByteString buffer if MultiByteCount is zero.
+ *
+ * @implemented
+ */
+INT STDCALL
+WideCharToMultiByte(UINT CodePage, DWORD Flags,
+                    LPCWSTR WideCharString, INT WideCharCount,
+                    LPSTR MultiByteString, INT MultiByteCount,
+                    LPCSTR DefaultChar, LPBOOL UsedDefaultChar)
+{
+   /* Check the parameters. */
+   if (WideCharString == NULL ||
+       (MultiByteString == NULL && MultiByteCount > 0))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return 0;
+   }
+
+   /* Determine the input string length. */
+   if (WideCharCount < 0)
+   {
+      WideCharCount = lstrlenW(WideCharString) + 1;
+   }
+
+   switch (CodePage)
+   {
+      case CP_UTF8:
+         return IntWideCharToMultiByteUTF8(
+            CodePage, Flags, WideCharString, WideCharCount,
+            MultiByteString, MultiByteCount, DefaultChar,
+            UsedDefaultChar);
+
+      case CP_UTF7:
+         DPRINT1("WideCharToMultiByte for CP_UTF7 is not implemented!\n");
+         return 0;
+
+      case CP_SYMBOL:
+         DPRINT1("WideCharToMultiByte for CP_SYMBOL is not implemented!\n");
+         return 0;
+
+      default:
+         return IntWideCharToMultiByteCP(
+            CodePage, Flags, WideCharString, WideCharCount,
+            MultiByteString, MultiByteCount, DefaultChar,
+            UsedDefaultChar);
+   }
+}
+
+/* EOF */