/*++

Copyright (c) 1991-2000,  Microsoft Corporation  All rights reserved.

Module Name:

    mbcs.c

Abstract:

    This file contains functions that convert multibyte character strings
    to wide character strings, convert wide character strings to multibyte
    character strings, convert a multibyte character string from one code
    page to a multibyte character string of another code page, and get the
    DBCS leadbyte ranges for a given code page.

    APIs found in this file:
      IsValidCodePage
      GetACP
      GetOEMCP
      GetCPInfo
      GetCPInfoExW
      IsDBCSLeadByte
      IsDBCSLeadByteEx
      MultiByteToWideChar
      WideCharToMultiByte

Revision History:

    05-31-91    JulieB    Created.

--*/



//
//  Include Files.
//

#include "nls.h"




//
//  Forward Declarations.
//

int
GetWCCompSB(
    PMB_TABLE pMBTbl,
    LPBYTE pMBStr,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr);

int
GetWCCompMB(
    PCP_HASH pHashN,
    PMB_TABLE pMBTbl,
    LPBYTE pMBStr,
    LPBYTE pEndMBStr,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    int *pmbIncr);

int
GetWCCompSBErr(
    PCP_HASH pHashN,
    PMB_TABLE pMBTbl,
    LPBYTE pMBStr,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr);

int
GetWCCompMBErr(
    PCP_HASH pHashN,
    PMB_TABLE pMBTbl,
    LPBYTE pMBStr,
    LPBYTE pEndMBStr,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    int *pmbIncr);

int
GetMBNoDefault(
    PCP_HASH pHashN,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    LPBYTE pMBStr,
    int cbMultiByte,
    DWORD dwFlags);

int
GetMBDefault(
    PCP_HASH pHashN,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    LPBYTE pMBStr,
    int cbMultiByte,
    WORD wDefault,
    LPBOOL pUsedDef,
    DWORD dwFlags);

int
GetMBDefaultComp(
    PCP_HASH pHashN,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    LPBYTE pMBStr,
    int cbMultiByte,
    WORD wDefault,
    LPBOOL pUsedDef,
    DWORD dwFlags);

int
GetMBCompSB(
    PCP_HASH pHashN,
    DWORD dwFlags,
    LPWSTR pWCStr,
    LPBYTE pMBStr,
    int mbCount,
    WORD wDefault,
    LPBOOL pUsedDef);

int
GetMBCompMB(
    PCP_HASH pHashN,
    DWORD dwFlags,
    LPWSTR pWCStr,
    LPBYTE pMBStr,
    int mbCount,
    WORD wDefault,
    LPBOOL pUsedDef,
    BOOL *fError,
    BOOL fOnlyOne);

UINT
GetMacCodePage(void);





//-------------------------------------------------------------------------//
//                           INTERNAL MACROS                               //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  CHECK_DBCS_LEAD_BYTE
//
//  Returns the offset to the DBCS table for the given leadbyte character.
//  If the given character is not a leadbyte, then it returns zero (table
//  value).
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define CHECK_DBCS_LEAD_BYTE(pDBCSOff, Ch)                                 \
    (pDBCSOff ? ((WORD)(pDBCSOff[Ch])) : ((WORD)0))


////////////////////////////////////////////////////////////////////////////
//
//  CHECK_ERROR_WC_SINGLE
//
//  Checks to see if the default character was used due to an invalid
//  character.  Sets last error and returns 0 characters written if an
//  invalid character was used.
//
//  NOTE: This macro may return if an error is encountered.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define CHECK_ERROR_WC_SINGLE( pHashN,                                     \
                               wch,                                        \
                               Ch )                                        \
{                                                                          \
    if ( ( (wch == pHashN->pCPInfo->wUniDefaultChar) &&                    \
           (Ch != pHashN->pCPInfo->wTransUniDefaultChar) ) ||              \
         ( (wch >= PRIVATE_USE_BEGIN) && (wch <= PRIVATE_USE_END) ) )      \
    {                                                                      \
        SetLastError(ERROR_NO_UNICODE_TRANSLATION);                        \
        return (0);                                                        \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  CHECK_ERROR_WC_MULTI
//
//  Checks to see if the default character was used due to an invalid
//  character.  Sets last error and returns 0 characters written if an
//  invalid character was used.
//
//  NOTE: This macro may return if an error is encountered.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define CHECK_ERROR_WC_MULTI( pHashN,                                      \
                              wch,                                         \
                              lead,                                        \
                              trail )                                      \
{                                                                          \
    if ((wch == pHashN->pCPInfo->wUniDefaultChar) &&                       \
        (MAKEWORD(trail, lead) != pHashN->pCPInfo->wTransUniDefaultChar))  \
    {                                                                      \
        SetLastError(ERROR_NO_UNICODE_TRANSLATION);                        \
        return (0);                                                        \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  CHECK_ERROR_WC_MULTI_SPECIAL
//
//  Checks to see if the default character was used due to an invalid
//  character.  Sets it to 0xffff if invalid.
//
//  DEFINED AS A MACRO.
//
//  08-21-95    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define CHECK_ERROR_WC_MULTI_SPECIAL( pHashN,                              \
                                      pWCStr,                              \
                                      lead,                                \
                                      trail )                              \
{                                                                          \
    if ((*pWCStr == pHashN->pCPInfo->wUniDefaultChar) &&                   \
        (MAKEWORD(trail, lead) != pHashN->pCPInfo->wTransUniDefaultChar))  \
    {                                                                      \
        *pWCStr = 0xffff;                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_WC_SINGLE
//
//  Fills in pWCStr with the wide character(s) for the corresponding single
//  byte character from the appropriate translation table.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_WC_SINGLE( pMBTbl,                                             \
                       pMBStr,                                             \
                       pWCStr )                                            \
{                                                                          \
    *pWCStr = pMBTbl[*pMBStr];                                             \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_WC_SINGLE_SPECIAL
//
//  Fills in pWCStr with the wide character(s) for the corresponding single
//  byte character from the appropriate translation table.  Also checks for
//  invalid characters - if invalid, it fills in 0xffff instead.
//
//  DEFINED AS A MACRO.
//
//  08-21-95    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_WC_SINGLE_SPECIAL( pHashN,                                     \
                               pMBTbl,                                     \
                               pMBStr,                                     \
                               pWCStr )                                    \
{                                                                          \
    *pWCStr = pMBTbl[*pMBStr];                                             \
                                                                           \
    if ( ( (*pWCStr == pHashN->pCPInfo->wUniDefaultChar) &&                \
           (*pMBStr != pHashN->pCPInfo->wTransUniDefaultChar) ) ||         \
         ( (*pWCStr >= PRIVATE_USE_BEGIN) &&                               \
           (*pWCStr <= PRIVATE_USE_END) ) )                                \
    {                                                                      \
        *pWCStr = 0xffff;                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_WC_MULTI
//
//  Fills in pWCStr with the wide character(s) for the corresponding multibyte
//  character from the appropriate translation table.  The number of bytes
//  used from the pMBStr buffer (single byte or double byte) is stored in
//  the mbIncr parameter.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_WC_MULTI( pHashN,                                              \
                      pMBTbl,                                              \
                      pMBStr,                                              \
                      pEndMBStr,                                           \
                      pWCStr,                                              \
                      pEndWCStr,                                           \
                      mbIncr )                                             \
{                                                                          \
    WORD Offset;                  /* offset to DBCS table for range */     \
                                                                           \
                                                                           \
    if (Offset = CHECK_DBCS_LEAD_BYTE(pHashN->pDBCSOffsets, *pMBStr))      \
    {                                                                      \
        /*                                                                 \
         *  DBCS Lead Byte.  Make sure there is a trail byte with the      \
         *  lead byte.                                                     \
         */                                                                \
        if (pMBStr + 1 == pEndMBStr)                                       \
        {                                                                  \
            /*                                                             \
             *  There is no trail byte with the lead byte.  The lead byte  \
             *  is the LAST character in the string.  Translate to NULL.   \
             */                                                            \
            *pWCStr = (WCHAR)0;                                            \
            mbIncr = 1;                                                    \
        }                                                                  \
        else if (*(pMBStr + 1) == 0)                                       \
        {                                                                  \
            /*                                                             \
             *  There is no trail byte with the lead byte.  The lead byte  \
             *  is followed by a NULL.  Translate to NULL.                 \
             *                                                             \
             *  Increment by 2 so that the null is not counted twice.      \
             */                                                            \
            *pWCStr = (WCHAR)0;                                            \
            mbIncr = 2;                                                    \
        }                                                                  \
        else                                                               \
        {                                                                  \
            /*                                                             \
             *  Fill in the wide character translation from the double     \
             *  byte character table.                                      \
             */                                                            \
            *pWCStr = (pHashN->pDBCSOffsets + Offset)[*(pMBStr + 1)];      \
            mbIncr = 2;                                                    \
        }                                                                  \
    }                                                                      \
    else                                                                   \
    {                                                                      \
        /*                                                                 \
         *  Not DBCS Lead Byte.  Fill in the wide character translation    \
         *  from the single byte character table.                          \
         */                                                                \
        *pWCStr = pMBTbl[*pMBStr];                                         \
        mbIncr = 1;                                                        \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_WC_MULTI_ERR
//
//  Fills in pWCStr with the wide character(s) for the corresponding multibyte
//  character from the appropriate translation table.  The number of bytes
//  used from the pMBStr buffer (single byte or double byte) is stored in
//  the mbIncr parameter.
//
//  Once the character has been translated, it checks to be sure the
//  character was valid.  If not, it sets last error and return 0 characters
//  written.
//
//  NOTE: This macro may return if an error is encountered.
//
//  DEFINED AS A MACRO.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_WC_MULTI_ERR( pHashN,                                          \
                          pMBTbl,                                          \
                          pMBStr,                                          \
                          pEndMBStr,                                       \
                          pWCStr,                                          \
                          pEndWCStr,                                       \
                          mbIncr )                                         \
{                                                                          \
    WORD Offset;                  /* offset to DBCS table for range */     \
                                                                           \
                                                                           \
    if (Offset = CHECK_DBCS_LEAD_BYTE(pHashN->pDBCSOffsets, *pMBStr))      \
    {                                                                      \
        /*                                                                 \
         *  DBCS Lead Byte.  Make sure there is a trail byte with the      \
         *  lead byte.                                                     \
         */                                                                \
        if ((pMBStr + 1 == pEndMBStr) || (*(pMBStr + 1) == 0))             \
        {                                                                  \
            /*                                                             \
             *  There is no trail byte with the lead byte.  Return error.  \
             */                                                            \
            SetLastError(ERROR_NO_UNICODE_TRANSLATION);                    \
            return (0);                                                    \
        }                                                                  \
                                                                           \
        /*                                                                 \
         *  Fill in the wide character translation from the double         \
         *  byte character table.                                          \
         */                                                                \
        *pWCStr = (pHashN->pDBCSOffsets + Offset)[*(pMBStr + 1)];          \
        mbIncr = 2;                                                        \
                                                                           \
        /*                                                                 \
         *  Make sure an invalid character was not translated to           \
         *  the default char.  Return an error if invalid.                 \
         */                                                                \
        CHECK_ERROR_WC_MULTI( pHashN,                                      \
                              *pWCStr,                                     \
                              *pMBStr,                                     \
                              *(pMBStr + 1) );                             \
    }                                                                      \
    else                                                                   \
    {                                                                      \
        /*                                                                 \
         *  Not DBCS Lead Byte.  Fill in the wide character translation    \
         *  from the single byte character table.                          \
         */                                                                \
        *pWCStr = pMBTbl[*pMBStr];                                         \
        mbIncr = 1;                                                        \
                                                                           \
        /*                                                                 \
         *  Make sure an invalid character was not translated to           \
         *  the default char.  Return an error if invalid.                 \
         */                                                                \
        CHECK_ERROR_WC_SINGLE( pHashN,                                     \
                               *pWCStr,                                    \
                               *pMBStr );                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_WC_MULTI_ERR_SPECIAL
//
//  Fills in pWCStr with the wide character(s) for the corresponding multibyte
//  character from the appropriate translation table.  The number of bytes
//  used from the pMBStr buffer (single byte or double byte) is stored in
//  the mbIncr parameter.
//
//  Once the character has been translated, it checks to be sure the
//  character was valid.  If not, it fills in 0xffff.
//
//  DEFINED AS A MACRO.
//
//  08-21-95    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_WC_MULTI_ERR_SPECIAL( pHashN,                                  \
                                  pMBTbl,                                  \
                                  pMBStr,                                  \
                                  pEndMBStr,                               \
                                  pWCStr,                                  \
                                  pEndWCStr,                               \
                                  mbIncr )                                 \
{                                                                          \
    WORD Offset;                  /* offset to DBCS table for range */     \
                                                                           \
                                                                           \
    if (Offset = CHECK_DBCS_LEAD_BYTE(pHashN->pDBCSOffsets, *pMBStr))      \
    {                                                                      \
        /*                                                                 \
         *  DBCS Lead Byte.  Make sure there is a trail byte with the      \
         *  lead byte.                                                     \
         */                                                                \
        if ((pMBStr + 1 == pEndMBStr) || (*(pMBStr + 1) == 0))             \
        {                                                                  \
            /*                                                             \
             *  There is no trail byte with the lead byte.  The lead byte  \
             *  is the LAST character in the string.  Translate to 0xffff. \
             */                                                            \
            *pWCStr = (WCHAR)0xffff;                                       \
            mbIncr = 1;                                                    \
        }                                                                  \
        else                                                               \
        {                                                                  \
            /*                                                             \
             *  Fill in the wide character translation from the double     \
             *  byte character table.                                      \
             */                                                            \
            *pWCStr = (pHashN->pDBCSOffsets + Offset)[*(pMBStr + 1)];      \
            mbIncr = 2;                                                    \
                                                                           \
            /*                                                             \
             *  Make sure an invalid character was not translated to       \
             *  the default char.  Translate to 0xffff if invalid.         \
             */                                                            \
            CHECK_ERROR_WC_MULTI_SPECIAL( pHashN,                          \
                                          pWCStr,                          \
                                          *pMBStr,                         \
                                          *(pMBStr + 1) );                 \
        }                                                                  \
    }                                                                      \
    else                                                                   \
    {                                                                      \
        /*                                                                 \
         *  Not DBCS Lead Byte.  Fill in the wide character translation    \
         *  from the single byte character table.                          \
         *  Make sure an invalid character was not translated to           \
         *  the default char.  Return an error if invalid.                 \
         */                                                                \
        GET_WC_SINGLE_SPECIAL( pHashN,                                     \
                               pMBTbl,                                     \
                               pMBStr,                                     \
                               pWCStr );                                   \
        mbIncr = 1;                                                        \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  COPY_MB_CHAR
//
//  Copies a multibyte character to the given string buffer.  If the
//  high byte of the multibyte word is zero, then it is a single byte
//  character and the number of characters written (returned) is 1.
//  Otherwise, it is a double byte character and the number of characters
//  written (returned) is 2.
//
//  NumByte will be 0 if the buffer is too small for the translation.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define COPY_MB_CHAR( mbChar,                                              \
                      pMBStr,                                              \
                      NumByte,                                             \
                      fOnlyOne )                                           \
{                                                                          \
    if (HIBYTE(mbChar))                                                    \
    {                                                                      \
        /*                                                                 \
         *  Make sure there is enough room in the buffer for both bytes.   \
         */                                                                \
        if (fOnlyOne)                                                      \
        {                                                                  \
            NumByte = 0;                                                   \
        }                                                                  \
        else                                                               \
        {                                                                  \
            /*                                                             \
             *  High Byte is NOT zero, so it's a DOUBLE byte char.         \
             *  Return 2 characters written.                               \
             */                                                            \
            *pMBStr = HIBYTE(mbChar);                                      \
            *(pMBStr + 1) = LOBYTE(mbChar);                                \
            NumByte = 2;                                                   \
        }                                                                  \
    }                                                                      \
    else                                                                   \
    {                                                                      \
        /*                                                                 \
         *  High Byte IS zero, so it's a SINGLE byte char.                 \
         *  Return 1 character written.                                    \
         */                                                                \
        *pMBStr = LOBYTE(mbChar);                                          \
        NumByte = 1;                                                       \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_SB
//
//  Fills in pMBStr with the single byte character for the corresponding
//  wide character from the appropriate translation table.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_SB( pWC,                                                       \
                wChar,                                                     \
                pMBStr )                                                   \
{                                                                          \
    *pMBStr = ((BYTE *)(pWC))[wChar];                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_MB
//
//  Fills in pMBStr with the multi byte character for the corresponding
//  wide character from the appropriate translation table.
//
//  mbCount will be 0 if the buffer is too small for the translation.
//
//    Broken Down Version:
//    --------------------
//        mbChar = ((WORD *)(pHashN->pWC))[wChar];
//        COPY_MB_CHAR(mbChar, pMBStr, mbCount);
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_MB( pWC,                                                       \
                wChar,                                                     \
                pMBStr,                                                    \
                mbCount,                                                   \
                fOnlyOne )                                                 \
{                                                                          \
    COPY_MB_CHAR( ((WORD *)(pWC))[wChar],                                  \
                  pMBStr,                                                  \
                  mbCount,                                                 \
                  fOnlyOne );                                              \
}


////////////////////////////////////////////////////////////////////////////
//
//  ELIMINATE_BEST_FIT_SB
//
//  Checks to see if a single byte Best Fit character was used.  If so,
//  it replaces it with a single byte default character.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define ELIMINATE_BEST_FIT_SB( pHashN,                                     \
                               wChar,                                      \
                               pMBStr )                                    \
{                                                                          \
    if ((pHashN->pMBTbl)[*pMBStr] != wChar)                                \
    {                                                                      \
        *pMBStr = LOBYTE(pHashN->pCPInfo->wDefaultChar);                   \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  ELIMINATE_BEST_FIT_MB
//
//  Checks to see if a multi byte Best Fit character was used.  If so,
//  it replaces it with a multi byte default character.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define ELIMINATE_BEST_FIT_MB( pHashN,                                     \
                               wChar,                                      \
                               pMBStr,                                     \
                               mbCount,                                    \
                               fOnlyOne )                                  \
{                                                                          \
    WORD Offset;                                                           \
    WORD wDefault;                                                         \
                                                                           \
    if (((mbCount == 1) && ((pHashN->pMBTbl)[*pMBStr] != wChar)) ||        \
        ((mbCount == 2) &&                                                 \
         (Offset = CHECK_DBCS_LEAD_BYTE(pHashN->pDBCSOffsets, *pMBStr)) && \
         (((pHashN->pDBCSOffsets + Offset)[*(pMBStr + 1)]) != wChar)))     \
    {                                                                      \
        wDefault = pHashN->pCPInfo->wDefaultChar;                          \
        if (HIBYTE(wDefault))                                              \
        {                                                                  \
            if (fOnlyOne)                                                  \
            {                                                              \
                mbCount = 0;                                               \
            }                                                              \
            else                                                           \
            {                                                              \
                *pMBStr = HIBYTE(wDefault);                                \
                *(pMBStr + 1) = LOBYTE(wDefault);                          \
                mbCount = 2;                                               \
            }                                                              \
        }                                                                  \
        else                                                               \
        {                                                                  \
            *pMBStr = LOBYTE(wDefault);                                    \
            mbCount = 1;                                                   \
        }                                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_DEFAULT_WORD
//
//  Takes a pointer to a character string (either one or two characters),
//  and converts it to a WORD value.  If the character is not DBCS, then it
//  zero extends the high byte.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_DEFAULT_WORD(pOff, pDefault)                                   \
    (CHECK_DBCS_LEAD_BYTE(pOff, *pDefault)                                 \
         ? MAKEWORD(*(pDefault + 1), *pDefault)                            \
         : MAKEWORD(*pDefault, 0))


////////////////////////////////////////////////////////////////////////////
//
//  DEFAULT_CHAR_CHECK_SB
//
//  Checks to see if the default character is used.  If it is, it sets
//  pUsedDef to TRUE (if non-null).  If the user specified a default, then
//  the user's default character is used.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define DEFAULT_CHAR_CHECK_SB( pHashN,                                     \
                               wch,                                        \
                               pMBStr,                                     \
                               wDefChar,                                   \
                               pUsedDef )                                  \
{                                                                          \
    WORD wSysDefChar = pHashN->pCPInfo->wDefaultChar;                      \
                                                                           \
                                                                           \
    /*                                                                     \
     *  Check for default character being used.                            \
     */                                                                    \
    if ((*pMBStr == (BYTE)wSysDefChar) &&                                  \
        (wch != pHashN->pCPInfo->wTransDefaultChar))                       \
    {                                                                      \
        /*                                                                 \
         *  Default was used.  Set the pUsedDef parameter to TRUE.         \
         */                                                                \
        *pUsedDef = TRUE;                                                  \
                                                                           \
        /*                                                                 \
         *  If the user specified a different default character than       \
         *  the system default, use that character instead.                \
         */                                                                \
        if (wSysDefChar != wDefChar)                                       \
        {                                                                  \
            *pMBStr = LOBYTE(wDefChar);                                    \
        }                                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  DEFAULT_CHAR_CHECK_MB
//
//  Checks to see if the default character is used.  If it is, it sets
//  pUsedDef to TRUE (if non-null).  If the user specified a default, then
//  the user's default character is used.  The number of bytes written to
//  the buffer is returned.
//
//  NumByte will be -1 if the buffer is too small for the translation.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define DEFAULT_CHAR_CHECK_MB( pHashN,                                     \
                               wch,                                        \
                               pMBStr,                                     \
                               wDefChar,                                   \
                               pUsedDef,                                   \
                               NumByte,                                    \
                               fOnlyOne )                                  \
{                                                                          \
    WORD wSysDefChar = pHashN->pCPInfo->wDefaultChar;                      \
                                                                           \
                                                                           \
    /*                                                                     \
     *  Set NumByte to zero for return (zero bytes written).               \
     */                                                                    \
    NumByte = 0;                                                           \
                                                                           \
    /*                                                                     \
     *  Check for default character being used.                            \
     */                                                                    \
    if ((*pMBStr == (BYTE)wSysDefChar) &&                                  \
        (wch != pHashN->pCPInfo->wTransDefaultChar))                       \
    {                                                                      \
        /*                                                                 \
         *  Default was used.  Set the pUsedDef parameter to TRUE.         \
         */                                                                \
        *pUsedDef = TRUE;                                                  \
                                                                           \
        /*                                                                 \
         *  If the user specified a different default character than       \
         *  the system default, use that character instead.                \
         */                                                                \
        if (wSysDefChar != wDefChar)                                       \
        {                                                                  \
            COPY_MB_CHAR( wDefChar,                                        \
                          pMBStr,                                          \
                          NumByte,                                         \
                          fOnlyOne );                                      \
            if (NumByte == 0)                                              \
            {                                                              \
                NumByte = -1;                                              \
            }                                                              \
        }                                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_WC_TRANSLATION_SB
//
//  Gets the 1:1 translation of a given wide character.  It fills in the
//  string pointer with the single byte character.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_WC_TRANSLATION_SB( pHashN,                                     \
                               wch,                                        \
                               pMBStr,                                     \
                               wDefault,                                   \
                               pUsedDef,                                   \
                               dwFlags )                                   \
{                                                                          \
    GET_SB( pHashN->pWC,                                                   \
            wch,                                                           \
            pMBStr );                                                      \
    if (dwFlags & WC_NO_BEST_FIT_CHARS)                                    \
    {                                                                      \
        ELIMINATE_BEST_FIT_SB( pHashN,                                     \
                               wch,                                        \
                               pMBStr );                                   \
    }                                                                      \
    DEFAULT_CHAR_CHECK_SB( pHashN,                                         \
                           wch,                                            \
                           pMBStr,                                         \
                           wDefault,                                       \
                           pUsedDef );                                     \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_WC_TRANSLATION_MB
//
//  Gets the 1:1 translation of a given wide character.  It fills in the
//  appropriate number of characters for the multibyte character and then
//  returns the number of characters written to the multibyte string.
//
//  mbCnt will be 0 if the buffer is too small for the translation.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_WC_TRANSLATION_MB( pHashN,                                     \
                               wch,                                        \
                               pMBStr,                                     \
                               wDefault,                                   \
                               pUsedDef,                                   \
                               mbCnt,                                      \
                               fOnlyOne,                                   \
                               dwFlags )                                   \
{                                                                          \
    int mbCnt2;              /* number of characters written */            \
                                                                           \
                                                                           \
    GET_MB( pHashN->pWC,                                                   \
            wch,                                                           \
            pMBStr,                                                        \
            mbCnt,                                                         \
            fOnlyOne );                                                    \
    if (dwFlags & WC_NO_BEST_FIT_CHARS)                                    \
    {                                                                      \
        ELIMINATE_BEST_FIT_MB( pHashN,                                     \
                               wch,                                        \
                               pMBStr,                                     \
                               mbCnt,                                      \
                               fOnlyOne );                                 \
    }                                                                      \
    if (mbCnt)                                                             \
    {                                                                      \
        DEFAULT_CHAR_CHECK_MB( pHashN,                                     \
                               wch,                                        \
                               pMBStr,                                     \
                               wDefault,                                   \
                               pUsedDef,                                   \
                               mbCnt2,                                     \
                               fOnlyOne );                                 \
        if (mbCnt2 == -1)                                                  \
        {                                                                  \
            mbCnt = 0;                                                     \
        }                                                                  \
        else if (mbCnt2)                                                   \
        {                                                                  \
            mbCnt = mbCnt2;                                                \
        }                                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_CP_HASH_NODE
//
//  Sets the code page value (if a special value is passed in) and the
//  hash node pointer.  If the code page value is invalid, the pointer
//  to the hash node will be set to NULL.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_CP_HASH_NODE( CodePage,                                        \
                          pHashN )                                         \
{                                                                          \
    PLOC_HASH pHashLoc;                                                    \
                                                                           \
                                                                           \
    /*                                                                     \
     *  Check for the ACP, OEMCP, or MACCP.  Fill in the appropriate       \
     *  value for the code page if one of these values is given.           \
     *  Otherwise, just get the hash node for the given code page.         \
     */                                                                    \
    if (CodePage == gAnsiCodePage)                                         \
    {                                                                      \
        pHashN = gpACPHashN;                                               \
    }                                                                      \
    else if (CodePage == gOemCodePage)                                     \
    {                                                                      \
        pHashN = gpOEMCPHashN;                                             \
    }                                                                      \
    else if (CodePage == CP_ACP)                                           \
    {                                                                      \
        CodePage = gAnsiCodePage;                                          \
        pHashN = gpACPHashN;                                               \
    }                                                                      \
    else if (CodePage == CP_OEMCP)                                         \
    {                                                                      \
        CodePage = gOemCodePage;                                           \
        pHashN = gpOEMCPHashN;                                             \
    }                                                                      \
    else if (CodePage == CP_SYMBOL)                                        \
    {                                                                      \
        pHashN = NULL;                                                     \
    }                                                                      \
    else if (CodePage == CP_MACCP)                                         \
    {                                                                      \
        CodePage = GetMacCodePage();                                       \
        pHashN = gpMACCPHashN;                                             \
    }                                                                      \
    else if (CodePage == CP_THREAD_ACP)                                    \
    {                                                                      \
        VALIDATE_LOCALE(NtCurrentTeb()->CurrentLocale, pHashLoc, FALSE);   \
        if (pHashLoc != NULL)                                              \
        {                                                                  \
            CodePage = pHashLoc->pLocaleFixed->DefaultACP;                 \
        }                                                                  \
        if (CodePage == CP_ACP)                                            \
        {                                                                  \
            CodePage = gAnsiCodePage;                                      \
            pHashN = gpACPHashN;                                           \
        }                                                                  \
        else if (CodePage == CP_OEMCP)                                     \
        {                                                                  \
            CodePage = gOemCodePage;                                       \
            pHashN = gpOEMCPHashN;                                         \
        }                                                                  \
        else if (CodePage == CP_MACCP)                                     \
        {                                                                  \
            CodePage = GetMacCodePage();                                   \
            pHashN = gpMACCPHashN;                                         \
        }                                                                  \
        else                                                               \
        {                                                                  \
            pHashN = GetCPHashNode(CodePage);                              \
        }                                                                  \
    }                                                                      \
    else                                                                   \
    {                                                                      \
        pHashN = GetCPHashNode(CodePage);                                  \
    }                                                                      \
}




//-------------------------------------------------------------------------//
//                             API ROUTINES                                //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  IsValidCodePage
//
//  Checks that the given code page is a valid one.  It does so by querying
//  the registry.  If the code page is found, then TRUE is returned.
//  Otherwise, FALSE is returned.
//
//  05-31-1991  JulieB      Created.
//  05-31-2002  ShawnSte    Make it not force the loading of the code page 
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI IsValidCodePage(
    UINT CodePage)
{
    //
    //  Do not allow special code page values to be valid here.
    //     (CP_ACP, CP_OEMCP, CP_MACCP, CP_THREAD_ACP, CP_SYMBOL are invalid)
    //

    //
    //  Do the quick check for the code page value equal to either
    //  the Ansi code page value or the OEM code page value.
    //
    if ((CodePage == gAnsiCodePage) || (CodePage == gOemCodePage) ||
        (CodePage == CP_UTF7) || (CodePage == CP_UTF8))
    {
        //
        //  Return success.
        //
        return (TRUE);
    }

    //
    //  Check for other code page values.
    //

    // If a node already exists, then we're OK
    if (IsCPHashNodeLoaded(CodePage) == TRUE)
    {
        //
        //  Return success.
        //
        return (TRUE);    
    }
    
    //
    //  Hash node doesn't exist.  Have to look in the registry.
    //  True if this works, false if it doesn't
    //
    return GetCPFileNameFromRegistry(CodePage, NULL, 0);
}

////////////////////////////////////////////////////////////////////////////
//
//  GetACP
//
//  Returns the ANSI code page for the system.  If the registry value is
//  not readable, then the chosen default ACP is used (NLS_DEFAULT_ACP).
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

UINT WINAPI GetACP()
{
    //
    //  Return the ACP stored in the cache.
    //
    return (gAnsiCodePage);
}


////////////////////////////////////////////////////////////////////////////
//
//  SetCPGlobal
//
//  Sets the code page global, used by Setup to force the code page into
//  the correct value during GUI mode.
//
//  02-15-99    JimSchm   Created.
////////////////////////////////////////////////////////////////////////////

UINT
WINAPI
SetCPGlobal (
    IN      UINT NewAcp
    )
{
    UINT oldVal;


    oldVal = gAnsiCodePage;

    //
    //  Sets the ACP global.  This is a private exported routine, not an API.
    //
    gAnsiCodePage = NewAcp;
    return oldVal;
}


////////////////////////////////////////////////////////////////////////////
//
//  GetOEMCP
//
//  Returns the OEM code page for the system.  If the registry value is
//  not readable, then the chosen default ACP is used (NLS_DEFAULT_OEMCP).
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

UINT WINAPI GetOEMCP()
{
    //
    //  Return the OEMCP stored in the cache.
    //
    return (gOemCodePage);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetCPInfo
//
//  Returns information about a given code page.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI GetCPInfo(
    UINT CodePage,
    LPCPINFO lpCPInfo)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    PCP_TABLE pInfo;              // ptr to CP information in file
    WORD wDefChar;                // default character
    BYTE *pLeadBytes;             // ptr to lead byte ranges
    UINT Ctr;                     // loop counter


    //
    //  See if it's a special code page value for UTF translations.
    //
    if (CodePage >= NLS_CP_ALGORITHM_RANGE)
    {
        return (UTFCPInfo(CodePage, lpCPInfo, FALSE));
    }

    //
    //  Get the code page value and the appropriate hash node.
    //
    GET_CP_HASH_NODE(CodePage, pHashN);

    //
    //  Invalid Parameter Check:
    //     - validate code page - get hash node containing translation tables
    //     - lpCPInfo is NULL
    //
    if ( (pHashN == NULL) ||
         ((pHashN->pCPInfo == NULL) && (pHashN->pfnCPProc == NULL)) ||
         (lpCPInfo == NULL) )
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (FALSE);
    }

    //
    //  See if the given code page is in the DLL range.
    //
    if (pHashN->pfnCPProc)
    {
        //
        //  Call the DLL to get the code page information.
        //
        return ( (*(pHashN->pfnCPProc))( CodePage,
                                         NLS_CP_CPINFO,
                                         NULL,
                                         0,
                                         NULL,
                                         0,
                                         lpCPInfo ) );
    }

    //
    //  Fill in the CPINFO structure with the appropriate information.
    //
    pInfo = pHashN->pCPInfo;

    //
    //  Get the max char size.
    //
    lpCPInfo->MaxCharSize = (UINT)((WORD)pInfo->MaxCharSize);

    //
    //  Get the default character.
    //
    wDefChar = pInfo->wDefaultChar;
    if (HIBYTE(wDefChar))
    {
        (lpCPInfo->DefaultChar)[0] = HIBYTE(wDefChar);
        (lpCPInfo->DefaultChar)[1] = LOBYTE(wDefChar);
    }
    else
    {
        (lpCPInfo->DefaultChar)[0] = LOBYTE(wDefChar);
        (lpCPInfo->DefaultChar)[1] = (BYTE)0;
    }

    //
    //  Get the leadbytes.
    //
    pLeadBytes = pInfo->LeadByte;
    for (Ctr = 0; Ctr < MAX_LEADBYTES; Ctr++)
    {
        (lpCPInfo->LeadByte)[Ctr] = pLeadBytes[Ctr];
    }

    //
    //  Return success.
    //
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetCPInfoExW
//
//  Returns information about a given code page.
//
//  11-15-96    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI GetCPInfoExW(
    UINT CodePage,
    DWORD dwFlags,
    LPCPINFOEXW lpCPInfoEx)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    PCP_TABLE pInfo;              // ptr to CP information in file
    WORD wDefChar;                // default character
    BYTE *pLeadBytes;             // ptr to lead byte ranges
    UINT Ctr;                     // loop counter


    //
    //  See if it's a special code page value for UTF translations.
    //
    if (CodePage >= NLS_CP_ALGORITHM_RANGE)
    {
        if (UTFCPInfo(CodePage, (LPCPINFO)lpCPInfoEx, TRUE))
        {
            if (GetStringTableEntry( CodePage,
                                     0,
                                     lpCPInfoEx->CodePageName,
                                     MAX_PATH,
                                     RC_CODE_PAGE_NAME ) != 0)
            {
                return (TRUE);
            }
        }
        return (FALSE);
    }

    //
    //  Get the code page value and the appropriate hash node.
    //
    GET_CP_HASH_NODE(CodePage, pHashN);

    //
    //  Invalid Parameter Check:
    //     - validate code page - get hash node containing translation tables
    //     - lpCPInfoEx is NULL
    //
    if ( (pHashN == NULL) ||
         ((pHashN->pCPInfo == NULL) && (pHashN->pfnCPProc == NULL)) ||
         (lpCPInfoEx == NULL) )
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (FALSE);
    }

    //
    //  Invalid Flags Check:
    //     - flags not 0
    //
    if (dwFlags != 0)
    {
        SetLastError(ERROR_INVALID_FLAGS);
        return (FALSE);
    }

    //
    //  See if the given code page is in the DLL range.
    //
    if (pHashN->pfnCPProc)
    {
        //
        //  Call the DLL to get the code page information.
        //
        if (((*(pHashN->pfnCPProc))( CodePage,
                                     NLS_CP_CPINFOEX,
                                     NULL,
                                     0,
                                     NULL,
                                     0,
                                     (LPCPINFO)lpCPInfoEx )) == TRUE)
        {
            return (TRUE);
        }
        else
        {
            //
            //  See if the CPINFO will succeed.  If so, then add the
            //  default CPINFOEX info to the structure.
            //
            if (((*(pHashN->pfnCPProc))( CodePage,
                                         NLS_CP_CPINFO,
                                         NULL,
                                         0,
                                         NULL,
                                         0,
                                         (LPCPINFO)lpCPInfoEx )) == TRUE)
            {
                //
                //  Fill in the Ex version info.
                //
                lpCPInfoEx->UnicodeDefaultChar = L'?';
                lpCPInfoEx->CodePage = CodePage;
                GetStringTableEntry( CodePage,
                                     0,
                                     lpCPInfoEx->CodePageName,
                                     MAX_PATH,
                                     RC_CODE_PAGE_NAME );

                SetLastError(NO_ERROR);
                return (TRUE);
            }

            return (FALSE);
        }
    }

    //
    //  Fill in the CPINFO structure with the appropriate information.
    //
    pInfo = pHashN->pCPInfo;

    //
    //  Get the max char size.
    //
    lpCPInfoEx->MaxCharSize = (UINT)((WORD)pInfo->MaxCharSize);

    //
    //  Get the default character.
    //
    wDefChar = pInfo->wDefaultChar;
    if (HIBYTE(wDefChar))
    {
        (lpCPInfoEx->DefaultChar)[0] = HIBYTE(wDefChar);
        (lpCPInfoEx->DefaultChar)[1] = LOBYTE(wDefChar);
    }
    else
    {
        (lpCPInfoEx->DefaultChar)[0] = LOBYTE(wDefChar);
        (lpCPInfoEx->DefaultChar)[1] = (BYTE)0;
    }

    //
    //  Get the leadbytes.
    //
    pLeadBytes = pInfo->LeadByte;
    for (Ctr = 0; Ctr < MAX_LEADBYTES; Ctr++)
    {
        (lpCPInfoEx->LeadByte)[Ctr] = pLeadBytes[Ctr];
    }

    //
    //  Get the Unicode default character.
    //
    lpCPInfoEx->UnicodeDefaultChar = pInfo->wUniDefaultChar;

    //
    //  Get the code page id.
    //
    lpCPInfoEx->CodePage = CodePage;

    //
    //  Get the code page name.
    //
    if (GetStringTableEntry( CodePage,
                             0,
                             lpCPInfoEx->CodePageName,
                             MAX_PATH,
                             RC_CODE_PAGE_NAME ) == 0)
    {
        return (FALSE);
    }

    //
    //  Return success.
    //
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  IsDBCSLeadByte
//
//  Checks to see if a given character is a DBCS lead byte in the ACP.
//  Returns TRUE if it is, FALSE if it is not.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI IsDBCSLeadByte(
    BYTE TestChar)
{
    //
    //  Get the hash node for the ACP.
    //
    if (gpACPHashN == NULL)
    {
        SetLastError(ERROR_FILE_NOT_FOUND);
        return (FALSE);
    }

    //
    //  See if the given character is a DBCS lead byte.
    //
    if (CHECK_DBCS_LEAD_BYTE(gpACPHashN->pDBCSOffsets, TestChar))
    {
        //
        //  Return success - IS a DBCS lead byte.
        //
        return (TRUE);
    }

    //
    //  Return failure - is NOT a DBCS lead byte.
    //
    return (FALSE);
}


////////////////////////////////////////////////////////////////////////////
//
//  IsDBCSLeadByteEx
//
//  Checks to see if a given character is a DBCS lead byte in the given
//  code page.  Returns TRUE if it is, FALSE if it is not.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI IsDBCSLeadByteEx(
    UINT CodePage,
    BYTE TestChar)
{
    PCP_HASH pHashN;              // ptr to CP hash node


    //
    //  See if it's a special code page value for UTF translations.
    //
    if (CodePage >= NLS_CP_ALGORITHM_RANGE)
    {
        //
        //  Return that it's not a DBCS leadbyte.
        //
        return (FALSE);
    }

    //
    //  Get the code page value and the appropriate hash node.
    //
    GET_CP_HASH_NODE(CodePage, pHashN);

    //
    //  Invalid Parameter Check:
    //     - validate code page
    //
    if (pHashN == NULL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (FALSE);
    }

    //
    //  See if the given character is a DBCS lead byte.
    //
    if (CHECK_DBCS_LEAD_BYTE(pHashN->pDBCSOffsets, TestChar))
    {
        //
        //  Return success - IS a DBCS lead byte.
        //
        return (TRUE);
    }

    //
    //  Return failure - is NOT a DBCS lead byte.
    //
    return (FALSE);
}


////////////////////////////////////////////////////////////////////////////
//
//  MultiByteToWideChar
//
//  Maps a multibyte character string to its wide character string
//  counterpart.
//
//  05-31-91    JulieB    Created.
//  09-01-93    JulieB    Add support for MB_ERR_INVALID_CHARS flag.
////////////////////////////////////////////////////////////////////////////

int WINAPI MultiByteToWideChar(
    UINT CodePage,
    DWORD dwFlags,
    LPCSTR lpMultiByteStr,
    int cbMultiByte,
    LPWSTR lpWideCharStr,
    int cchWideChar)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    register LPBYTE pMBStr;       // ptr to search through MB string
    register LPWSTR pWCStr;       // ptr to search through WC string
    LPBYTE pEndMBStr;             // ptr to end of MB search string
    LPWSTR pEndWCStr;             // ptr to end of WC string buffer
    int wcIncr;                   // amount to increment pWCStr
    int mbIncr;                   // amount to increment pMBStr
    int wcCount = 0;              // count of wide chars written
    int CompSet;                  // if MB_COMPOSITE flag is set
    PMB_TABLE pMBTbl;             // ptr to correct MB table (MB or GLYPH)
    int ctr;                      // loop counter


    //
    //  See if it's a special code page value for UTF translations.
    //
    if (CodePage >= NLS_CP_ALGORITHM_RANGE)
    {
        return (UTFToUnicode( CodePage,
                              dwFlags,
                              lpMultiByteStr,
                              cbMultiByte,
                              lpWideCharStr,
                              cchWideChar ));
    }

    //
    //  Get the code page value and the appropriate hash node.
    //
    GET_CP_HASH_NODE(CodePage, pHashN);

    //
    //  Invalid Parameter Check:
    //     - length of MB string is 0
    //     - wide char buffer size is negative
    //     - MB string is NULL
    //     - length of WC string is NOT zero AND
    //         (WC string is NULL OR src and dest pointers equal)
    //
    if ( (cbMultiByte == 0) || (cchWideChar < 0) ||
         (lpMultiByteStr == NULL) ||
         ((cchWideChar != 0) &&
          ((lpWideCharStr == NULL) ||
           (lpMultiByteStr == (LPSTR)lpWideCharStr))) )
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  If cbMultiByte is -1, then the string is null terminated and we
    //  need to get the length of the string.  Add one to the length to
    //  include the null termination.  (This will always be at least 1.)
    //
    if (cbMultiByte <= -1)
    {
        cbMultiByte = strlen(lpMultiByteStr) + 1;
    }

    //
    //  Check for valid code page.
    //
    if (pHashN == NULL)
    {
        //
        //  Special case the CP_SYMBOL code page.
        //
        if ((CodePage == CP_SYMBOL) && (dwFlags == 0))
        {
            //
            //  If the caller just wants the size of the buffer needed
            //  to do this translation, return the size of the MB string.
            //
            if (cchWideChar == 0)
            {
                return (cbMultiByte);
            }

            //
            //  Make sure the buffer is large enough.
            //
            if (cchWideChar < cbMultiByte)
            {
                SetLastError(ERROR_INSUFFICIENT_BUFFER);
                return (0);
            }

            //
            //  Translate SB char xx to Unicode f0xx.
            //    0x00->0x1f map to 0x0000->0x001f
            //    0x20->0xff map to 0xf020->0xf0ff
            //
            for (ctr = 0; ctr < cbMultiByte; ctr++)
            {
                lpWideCharStr[ctr] = ((BYTE)(lpMultiByteStr[ctr]) < 0x20)
                                       ? (WCHAR)lpMultiByteStr[ctr]
                                       : MAKEWORD(lpMultiByteStr[ctr], 0xf0);
            }
            return (cbMultiByte);
        }
        else
        {
            SetLastError(((CodePage == CP_SYMBOL) && (dwFlags != 0))
                           ? ERROR_INVALID_FLAGS
                           : ERROR_INVALID_PARAMETER);
            return (0);
        }
    }

    //
    //  See if the given code page is in the DLL range.
    //
    if (pHashN->pfnCPProc)
    {
        //
        //  Invalid Flags Check:
        //     - flags not 0
        //
        if (dwFlags != 0)
        {
            SetLastError(ERROR_INVALID_FLAGS);
            return (0);
        }

        //
        //  Call the DLL to do the translation.
        //
        return ( (*(pHashN->pfnCPProc))( CodePage,
                                         NLS_CP_MBTOWC,
                                         (LPSTR)lpMultiByteStr,
                                         cbMultiByte,
                                         (LPWSTR)lpWideCharStr,
                                         cchWideChar,
                                         NULL ) );
    }

    //
    //  Invalid Flags Check:
    //     - flags other than valid ones
    //     - composite and precomposed both set
    //
    if ( (dwFlags & MB_INVALID_FLAG) ||
         ((dwFlags & MB_PRECOMPOSED) && (dwFlags & MB_COMPOSITE)) )
    {
        SetLastError(ERROR_INVALID_FLAGS);
        return (0);
    }

    //
    //  Initialize multibyte character loop pointers.
    //
    pMBStr = (LPBYTE)lpMultiByteStr;
    pEndMBStr = pMBStr + cbMultiByte;
    CompSet = dwFlags & MB_COMPOSITE;

    //
    //  Get the correct MB table (MB or GLYPH).
    //
    if ((dwFlags & MB_USEGLYPHCHARS) && (pHashN->pGlyphTbl != NULL))
    {
        pMBTbl = pHashN->pGlyphTbl;
    }
    else
    {
        pMBTbl = pHashN->pMBTbl;
    }

    //
    //  If cchWideChar is 0, then we can't use lpWideCharStr.  In this
    //  case, we simply want to count the number of characters that would
    //  be written to the buffer.
    //
    if (cchWideChar == 0)
    {
        WCHAR pTempStr[MAX_COMPOSITE];   // tmp buffer - max for composite

        //
        //  For each multibyte char, translate it to its corresponding
        //  wide char and increment the wide character count.
        //
        pEndWCStr = pTempStr + MAX_COMPOSITE;
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            if (CompSet)
            {
                //
                //  Composite flag is set.
                //
                if (dwFlags & MB_ERR_INVALID_CHARS)
                {
                    //
                    //  Error check flag is set.
                    //
                    while (pMBStr < pEndMBStr)
                    {
                        if (!(wcIncr = GetWCCompSBErr( pHashN,
                                                       pMBTbl,
                                                       pMBStr,
                                                       pTempStr,
                                                       pEndWCStr )))
                        {
                            return (0);
                        }
                        pMBStr++;
                        wcCount += wcIncr;
                    }
                }
                else
                {
                    //
                    //  Error check flag is NOT set.
                    //
                    while (pMBStr < pEndMBStr)
                    {
                        wcCount += GetWCCompSB( pMBTbl,
                                                pMBStr,
                                                pTempStr,
                                                pEndWCStr );
                        pMBStr++;
                    }
                }
            }
            else
            {
                //
                //  Composite flag is NOT set.
                //
                if (dwFlags & MB_ERR_INVALID_CHARS)
                {
                    //
                    //  Error check flag is set.
                    //
                    wcCount = (int)(pEndMBStr - pMBStr);
                    while (pMBStr < pEndMBStr)
                    {
                        GET_WC_SINGLE( pMBTbl,
                                       pMBStr,
                                       pTempStr );
                        CHECK_ERROR_WC_SINGLE( pHashN,
                                               *pTempStr,
                                               *pMBStr );
                        pMBStr++;
                    }
                }
                else
                {
                    //
                    //  Error check flag is NOT set.
                    //
                    //  Just return the size of the MB string, since
                    //  it's a 1:1 translation.
                    //
                    wcCount = (int)(pEndMBStr - pMBStr);
                }
            }
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            if (CompSet)
            {
                //
                //  Composite flag is set.
                //
                if (dwFlags & MB_ERR_INVALID_CHARS)
                {
                    //
                    //  Error check flag is set.
                    //
                    while (pMBStr < pEndMBStr)
                    {
                        if (!(wcIncr = GetWCCompMBErr( pHashN,
                                                       pMBTbl,
                                                       pMBStr,
                                                       pEndMBStr,
                                                       pTempStr,
                                                       pEndWCStr,
                                                       &mbIncr )))
                        {
                            return (0);
                        }
                        pMBStr += mbIncr;
                        wcCount += wcIncr;
                    }
                }
                else
                {
                    //
                    //  Error check flag is NOT set.
                    //
                    while (pMBStr < pEndMBStr)
                    {
                        wcCount += GetWCCompMB( pHashN,
                                                pMBTbl,
                                                pMBStr,
                                                pEndMBStr,
                                                pTempStr,
                                                pEndWCStr,
                                                &mbIncr );
                        pMBStr += mbIncr;
                    }
                }
            }
            else
            {
                //
                //  Composite flag is NOT set.
                //
                if (dwFlags & MB_ERR_INVALID_CHARS)
                {
                    //
                    //  Error check flag is set.
                    //
                    while (pMBStr < pEndMBStr)
                    {
                        GET_WC_MULTI_ERR( pHashN,
                                          pMBTbl,
                                          pMBStr,
                                          pEndMBStr,
                                          pTempStr,
                                          pEndWCStr,
                                          mbIncr );
                        pMBStr += mbIncr;
                        wcCount++;
                    }
                }
                else
                {
                    //
                    //  Error check flag is NOT set.
                    //
                    while (pMBStr < pEndMBStr)
                    {
                        GET_WC_MULTI( pHashN,
                                      pMBTbl,
                                      pMBStr,
                                      pEndMBStr,
                                      pTempStr,
                                      pEndWCStr,
                                      mbIncr );
                        pMBStr += mbIncr;
                        wcCount++;
                    }
                }
            }
        }
    }
    else
    {
        //
        //  Initialize wide character loop pointers.
        //
        pWCStr = lpWideCharStr;
        pEndWCStr = pWCStr + cchWideChar;

        //
        //  For each multibyte char, translate it to its corresponding
        //  wide char, store it in lpWideCharStr, and increment the wide
        //  character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            if (CompSet)
            {
                //
                //  Composite flag is set.
                //
                if (dwFlags & MB_ERR_INVALID_CHARS)
                {
                    //
                    //  Error check flag is set.
                    //
                    while ((pMBStr < pEndMBStr) && (pWCStr < pEndWCStr))
                    {
                        if (!(wcIncr = GetWCCompSBErr( pHashN,
                                                       pMBTbl,
                                                       pMBStr,
                                                       pWCStr,
                                                       pEndWCStr )))
                        {
                            return (0);
                        }
                        pMBStr++;
                        pWCStr += wcIncr;
                    }
                    wcCount = (int)(pWCStr - lpWideCharStr);
                }
                else
                {
                    //
                    //  Error check flag is NOT set.
                    //
                    while ((pMBStr < pEndMBStr) && (pWCStr < pEndWCStr))
                    {
                        pWCStr += GetWCCompSB( pMBTbl,
                                               pMBStr,
                                               pWCStr,
                                               pEndWCStr );
                        pMBStr++;
                    }
                    wcCount = (int)(pWCStr - lpWideCharStr);
                }
            }
            else
            {
                //
                //  Composite flag is NOT set.
                //
                if (dwFlags & MB_ERR_INVALID_CHARS)
                {
                    //
                    //  Error check flag is set.
                    //
                    wcCount = (int)(pEndMBStr - pMBStr);
                    if ((pEndWCStr - pWCStr) < wcCount)
                    {
                        wcCount = (int)(pEndWCStr - pWCStr);
                    }
                    for (ctr = wcCount; ctr > 0; ctr--)
                    {
                        GET_WC_SINGLE( pMBTbl,
                                       pMBStr,
                                       pWCStr );
                        CHECK_ERROR_WC_SINGLE( pHashN,
                                               *pWCStr,
                                               *pMBStr );
                        pMBStr++;
                        pWCStr++;
                    }
                }
                else
                {
                    //
                    //  Error check flag is NOT set.
                    //
                    wcCount = (int)(pEndMBStr - pMBStr);
                    if ((pEndWCStr - pWCStr) < wcCount)
                    {
                        wcCount = (int)(pEndWCStr - pWCStr);
                    }
                    for (ctr = wcCount; ctr > 0; ctr--)
                    {
                        GET_WC_SINGLE( pMBTbl,
                                       pMBStr,
                                       pWCStr );
                        pMBStr++;
                        pWCStr++;
                    }
                }
            }
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            if (CompSet)
            {
                //
                //  Composite flag is set.
                //
                if (dwFlags & MB_ERR_INVALID_CHARS)
                {
                    //
                    //  Error check flag is set.
                    //
                    while ((pMBStr < pEndMBStr) && (pWCStr < pEndWCStr))
                    {
                        if (!(wcIncr = GetWCCompMBErr( pHashN,
                                                       pMBTbl,
                                                       pMBStr,
                                                       pEndMBStr,
                                                       pWCStr,
                                                       pEndWCStr,
                                                       &mbIncr )))
                        {
                            return (0);
                        }
                        pMBStr += mbIncr;
                        pWCStr += wcIncr;
                    }
                    wcCount = (int)(pWCStr - lpWideCharStr);
                }
                else
                {
                    //
                    //  Error check flag is NOT set.
                    //
                    while ((pMBStr < pEndMBStr) && (pWCStr < pEndWCStr))
                    {
                        pWCStr += GetWCCompMB( pHashN,
                                               pMBTbl,
                                               pMBStr,
                                               pEndMBStr,
                                               pWCStr,
                                               pEndWCStr,
                                               &mbIncr );
                        pMBStr += mbIncr;
                    }
                    wcCount = (int)(pWCStr - lpWideCharStr);
                }
            }
            else
            {
                //
                //  Composite flag is NOT set.
                //
                if (dwFlags & MB_ERR_INVALID_CHARS)
                {
                    //
                    //  Error check flag is set.
                    //
                    while ((pMBStr < pEndMBStr) && (pWCStr < pEndWCStr))
                    {
                        GET_WC_MULTI_ERR( pHashN,
                                          pMBTbl,
                                          pMBStr,
                                          pEndMBStr,
                                          pWCStr,
                                          pEndWCStr,
                                          mbIncr );
                        pMBStr += mbIncr;
                        pWCStr++;
                    }
                    wcCount = (int)(pWCStr - lpWideCharStr);
                }
                else
                {
                    //
                    //  Error check flag is NOT set.
                    //
                    while ((pMBStr < pEndMBStr) && (pWCStr < pEndWCStr))
                    {
                        GET_WC_MULTI( pHashN,
                                      pMBTbl,
                                      pMBStr,
                                      pEndMBStr,
                                      pWCStr,
                                      pEndWCStr,
                                      mbIncr );
                        pMBStr += mbIncr;
                        pWCStr++;
                    }
                    wcCount = (int)(pWCStr - lpWideCharStr);
                }
            }
        }

        //
        //  Make sure wide character buffer was large enough.
        //
        if (pMBStr < pEndMBStr)
        {
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return (0);
        }
    }

    //
    //  Return the number of characters written (or that would have
    //  been written) to the buffer.
    //
    return (wcCount);
}


////////////////////////////////////////////////////////////////////////////
//
//  WideCharToMultiByte
//
//  Maps a wide character string to its multibyte character string
//  counterpart.
//
//  NOTE:  Most significant bit of dwFlags parameter is used by this routine
//         to indicate that the caller only wants the count of the number of
//         characters written, not the string (ie. do not back up in buffer).
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI WideCharToMultiByte(
    UINT CodePage,
    DWORD dwFlags,
    LPCWSTR lpWideCharStr,
    int cchWideChar,
    LPSTR lpMultiByteStr,
    int cbMultiByte,
    LPCSTR lpDefaultChar,
    LPBOOL lpUsedDefaultChar)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    LPWSTR pWCStr;                // ptr to search through WC string
    LPWSTR pEndWCStr;             // ptr to end of WC string buffer
    WORD wDefault = 0;            // default character as a word
    int IfNoDefault;              // if default check is to be made
    int IfCompositeChk;           // if check for composite
    BOOL TmpUsed;                 // temp storage for default used
    int ctr;                      // loop counter


    //
    //  See if it's a special code page value for UTF translations.
    //
    if (CodePage >= NLS_CP_ALGORITHM_RANGE)
    {
        return (UnicodeToUTF( CodePage,
                              dwFlags,
                              lpWideCharStr,
                              cchWideChar,
                              lpMultiByteStr,
                              cbMultiByte,
                              lpDefaultChar,
                              lpUsedDefaultChar ));
    }

    //
    //  Get the code page value and the appropriate hash node.
    //
    GET_CP_HASH_NODE(CodePage, pHashN);

    //
    //  Invalid Parameter Check:
    //     - length of WC string is 0
    //     - multibyte buffer size is negative
    //     - WC string is NULL
    //     - length of WC string is NOT zero AND
    //         (MB string is NULL OR src and dest pointers equal)
    //
    if ( (cchWideChar == 0) || (cbMultiByte < 0) ||
         (lpWideCharStr == NULL) ||
         ((cbMultiByte != 0) &&
          ((lpMultiByteStr == NULL) ||
           (lpWideCharStr == (LPWSTR)lpMultiByteStr))) )
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  If cchWideChar is -1, then the string is null terminated and we
    //  need to get the length of the string.  Add one to the length to
    //  include the null termination.  (This will always be at least 1.)
    //
    if (cchWideChar <= -1)
    {
        cchWideChar = NlsStrLenW(lpWideCharStr) + 1;
    }

    //
    //  Check for valid code page.
    //
    if (pHashN == NULL)
    {
        //
        //  Special case the CP_SYMBOL code page.
        //
        if ((CodePage == CP_SYMBOL) && (dwFlags == 0) &&
            (lpDefaultChar == NULL) && (lpUsedDefaultChar == NULL))
        {
            //
            //  If the caller just wants the size of the buffer needed
            //  to do this translation, return the size of the MB string.
            //
            if (cbMultiByte == 0)
            {
                return (cchWideChar);
            }

            //
            //  Make sure the buffer is large enough.
            //
            if (cbMultiByte < cchWideChar)
            {
                SetLastError(ERROR_INSUFFICIENT_BUFFER);
                return (0);
            }

            //
            //  Translate Unicode char f0xx to SB xx.
            //    0x0000->0x001f map to 0x00->0x1f
            //    0xf020->0xf0ff map to 0x20->0xff
            //
            for (ctr = 0; ctr < cchWideChar; ctr++)
            {
                if ((lpWideCharStr[ctr] >= 0x0020) &&
                    ((lpWideCharStr[ctr] < 0xf020) ||
                     (lpWideCharStr[ctr] > 0xf0ff)))
                {
                    SetLastError(ERROR_NO_UNICODE_TRANSLATION);                        \
                    return (0);
                }
                lpMultiByteStr[ctr] = (BYTE)lpWideCharStr[ctr];
            }
            return (cchWideChar);
        }
        else
        {
            SetLastError(((CodePage == CP_SYMBOL) && (dwFlags != 0))
                           ? ERROR_INVALID_FLAGS
                           : ERROR_INVALID_PARAMETER);
            return (0);
        }
    }

    //
    //  See if the given code page is in the DLL range.
    //
    if (pHashN->pfnCPProc)
    {
        //
        //  Invalid Parameter Check:
        //     - lpDefaultChar not NULL
        //     - lpUsedDefaultChar not NULL
        //
        if ((lpDefaultChar != NULL) || (lpUsedDefaultChar != NULL))
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            return (0);
        }

        //
        //  Invalid Flags Check:
        //     - flags not 0
        //
        if (dwFlags != 0)
        {
            SetLastError(ERROR_INVALID_FLAGS);
            return (0);
        }

        //
        //  Call the DLL to do the translation.
        //
        return ( (*(pHashN->pfnCPProc))( CodePage,
                                         NLS_CP_WCTOMB,
                                         (LPSTR)lpMultiByteStr,
                                         cbMultiByte,
                                         (LPWSTR)lpWideCharStr,
                                         cchWideChar,
                                         NULL ) );
    }

    //
    //  Invalid Flags Check:
    //     - compositechk flag is not set AND any of comp flags are set
    //     - flags other than valid ones
    //
    if ( ((!(IfCompositeChk = (dwFlags & WC_COMPOSITECHECK))) &&
          (dwFlags & WC_COMPCHK_FLAGS)) ||
         (dwFlags & WC_INVALID_FLAG) )
    {
        SetLastError(ERROR_INVALID_FLAGS);
        return (0);
    }

    //
    //  Initialize wide character loop pointers.
    //
    pWCStr = (LPWSTR)lpWideCharStr;
    pEndWCStr = pWCStr + cchWideChar;

    //
    //  Set the IfNoDefault parameter to TRUE if both lpDefaultChar and
    //  lpUsedDefaultChar are NULL.
    //
    IfNoDefault = ((lpDefaultChar == NULL) && (lpUsedDefaultChar == NULL));

    //
    //  If the composite check flag is NOT set AND both of the default
    //  parameters (lpDefaultChar and lpUsedDefaultChar) are null, then
    //  do the quick translation.
    //
    if (IfNoDefault && !IfCompositeChk)
    {
        //
        //  Translate WC string to MB string, ignoring default chars.
        //
        return (GetMBNoDefault( pHashN,
                                pWCStr,
                                pEndWCStr,
                                (LPBYTE)lpMultiByteStr,
                                cbMultiByte,
                                dwFlags ));
    }

    //
    //  Set the system default character.
    //
    wDefault = pHashN->pCPInfo->wDefaultChar;

    //
    //  See if the default check is needed.
    //
    if (!IfNoDefault)
    {
        //
        //  If lpDefaultChar is NULL, then use the system default.
        //  Form a word out of the default character.  Single byte
        //  characters are zero extended, DBCS characters are as is.
        //
        if (lpDefaultChar != NULL)
        {
            wDefault = GET_DEFAULT_WORD( pHashN->pDBCSOffsets,
                                         (LPBYTE)lpDefaultChar );
        }

        //
        //  If lpUsedDefaultChar is NULL, then it won't be used later
        //  on if a default character is detected.  Otherwise, we need
        //  to initialize it.
        //
        if (lpUsedDefaultChar == NULL)
        {
            lpUsedDefaultChar = &TmpUsed;
        }
        *lpUsedDefaultChar = FALSE;

        //
        //  Check for "composite check" flag.
        //
        if (!IfCompositeChk)
        {
            //
            //  Translate WC string to MB string, checking for the use of the
            //  default character.
            //
            return (GetMBDefault( pHashN,
                                  pWCStr,
                                  pEndWCStr,
                                  (LPBYTE)lpMultiByteStr,
                                  cbMultiByte,
                                  wDefault,
                                  lpUsedDefaultChar,
                                  dwFlags ));
        }
        else
        {
            //
            //  Translate WC string to MB string, checking for the use of the
            //  default character.
            //
            return (GetMBDefaultComp( pHashN,
                                      pWCStr,
                                      pEndWCStr,
                                      (LPBYTE)lpMultiByteStr,
                                      cbMultiByte,
                                      wDefault,
                                      lpUsedDefaultChar,
                                      dwFlags ));
        }
    }
    else
    {
        //
        //  The only case left here is that the Composite check
        //  flag IS set and the default check flag is NOT set.
        //
        //  Translate WC string to MB string, checking for the use of the
        //  default character.
        //
        return (GetMBDefaultComp( pHashN,
                                  pWCStr,
                                  pEndWCStr,
                                  (LPBYTE)lpMultiByteStr,
                                  cbMultiByte,
                                  wDefault,
                                  &TmpUsed,
                                  dwFlags ));
    }
}




//-------------------------------------------------------------------------//
//                          INTERNAL ROUTINES                              //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  GetWCCompSB
//
//  Fills in pWCStr with the wide character(s) for the corresponding single
//  byte character from the appropriate translation table and returns the
//  number of wide characters written.  This routine should only be called
//  when the precomposed forms need to be translated to composite.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetWCCompSB(
    PMB_TABLE pMBTbl,
    LPBYTE pMBStr,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr)
{
    //
    //  Get the single byte to wide character translation.
    //
    GET_WC_SINGLE(pMBTbl, pMBStr, pWCStr);

    //
    //  Fill in the composite form of the character (if one exists)
    //  and return the number of wide characters written.
    //
    return (InsertCompositeForm(pWCStr, pEndWCStr));
}


////////////////////////////////////////////////////////////////////////////
//
//  GetWCCompMB
//
//  Fills in pWCStr with the wide character(s) for the corresponding multibyte
//  character from the appropriate translation table and returns the number
//  of wide characters written.  The number of bytes used from the pMBStr
//  buffer (single byte or double byte) is returned in the mbIncr parameter.
//  This routine should only be called when the precomposed forms need to be
//  translated to composite.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetWCCompMB(
    PCP_HASH pHashN,
    PMB_TABLE pMBTbl,
    LPBYTE pMBStr,
    LPBYTE pEndMBStr,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    int *pmbIncr)
{
    //
    //  Get the multibyte to wide char translation.
    //
    GET_WC_MULTI( pHashN,
                  pMBTbl,
                  pMBStr,
                  pEndMBStr,
                  pWCStr,
                  pEndWCStr,
                  *pmbIncr );

    //
    //  Fill in the composite form of the character (if one exists)
    //  and return the number of wide characters written.
    //
    return (InsertCompositeForm(pWCStr, pEndWCStr));
}


////////////////////////////////////////////////////////////////////////////
//
//  GetWCCompSBErr
//
//  Fills in pWCStr with the wide character(s) for the corresponding single
//  byte character from the appropriate translation table and returns the
//  number of wide characters written.  This routine should only be called
//  when the precomposed forms need to be translated to composite.
//
//  Checks to be sure an invalid character is not translated to the default
//  character.  If so, it sets last error and returns 0 characters written.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetWCCompSBErr(
    PCP_HASH pHashN,
    PMB_TABLE pMBTbl,
    LPBYTE pMBStr,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr)
{
    //
    //  Get the single byte to wide character translation.
    //
    GET_WC_SINGLE(pMBTbl, pMBStr, pWCStr);

    //
    //  Make sure an invalid character was not translated to the
    //  default char.  If it was, set last error and return 0
    //  characters written.
    //
    CHECK_ERROR_WC_SINGLE(pHashN, *pWCStr, *pMBStr);

    //
    //  Fill in the composite form of the character (if one exists)
    //  and return the number of wide characters written.
    //
    return (InsertCompositeForm(pWCStr, pEndWCStr));
}


////////////////////////////////////////////////////////////////////////////
//
//  GetWCCompMBErr
//
//  Fills in pWCStr with the wide character(s) for the corresponding multibyte
//  character from the appropriate translation table and returns the number
//  of wide characters written.  The number of bytes used from the pMBStr
//  buffer (single byte or double byte) is returned in the mbIncr parameter.
//  This routine should only be called when the precomposed forms need to be
//  translated to composite.
//
//  Checks to be sure an invalid character is not translated to the default
//  character.  If so, it sets last error and returns 0 characters written.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetWCCompMBErr(
    PCP_HASH pHashN,
    PMB_TABLE pMBTbl,
    LPBYTE pMBStr,
    LPBYTE pEndMBStr,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    int *pmbIncr)
{
    //
    //  Get the multibyte to wide char translation.
    //
    //  Make sure an invalid character was not translated to the
    //  default char.  If it was, set last error and return 0
    //  characters written.
    //
    GET_WC_MULTI_ERR( pHashN,
                      pMBTbl,
                      pMBStr,
                      pEndMBStr,
                      pWCStr,
                      pEndWCStr,
                      *pmbIncr );

    //
    //  Fill in the composite form of the character (if one exists)
    //  and return the number of wide characters written.
    //
    return (InsertCompositeForm(pWCStr, pEndWCStr));
}


////////////////////////////////////////////////////////////////////////////
//
//  GetMBNoDefault
//
//  Translates the wide character string to a multibyte string and returns
//  the number of bytes written.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetMBNoDefault(
    PCP_HASH pHashN,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    LPBYTE pMBStr,
    int cbMultiByte,
    DWORD dwFlags)
{
    int mbIncr;                   // amount to increment pMBStr
    int mbCount = 0;              // count of multibyte chars written
    LPBYTE pEndMBStr;             // ptr to end of MB string buffer
    PWC_TABLE pWC = pHashN->pWC;  // ptr to WC table
    int ctr;                      // loop counter


    //
    //  If cbMultiByte is 0, then we can't use pMBStr.  In this
    //  case, we simply want to count the number of characters that
    //  would be written to the buffer.
    //
    if (cbMultiByte == 0)
    {
        BYTE pTempStr[2];             // tmp buffer - 2 bytes for DBCS

        //
        //  For each wide char, translate it to its corresponding multibyte
        //  char and increment the multibyte character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            //  Just return the count of characters - it will be the
            //  same number of characters as the source string.
            //
            mbCount = (int)(pEndWCStr - pWCStr);
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            if (dwFlags & WC_NO_BEST_FIT_CHARS)
            {
                while (pWCStr < pEndWCStr)
                {
                    GET_MB( pWC,
                            *pWCStr,
                            pTempStr,
                            mbIncr,
                            FALSE );
                    ELIMINATE_BEST_FIT_MB( pHashN,
                                           *pWCStr,
                                           pTempStr,
                                           mbIncr,
                                           FALSE );
                    pWCStr++;
                    mbCount += mbIncr;
                }
            }
            else
            {
                while (pWCStr < pEndWCStr)
                {
                    GET_MB( pWC,
                            *pWCStr,
                            pTempStr,
                            mbIncr,
                            FALSE );
                    pWCStr++;
                    mbCount += mbIncr;
                }
            }
        }
    }
    else
    {
        //
        //  Initialize multibyte loop pointers.
        //
        pEndMBStr = pMBStr + cbMultiByte;

        //
        //  For each wide char, translate it to its corresponding
        //  multibyte char, store it in pMBStr, and increment the
        //  multibyte character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            mbCount = (int)(pEndWCStr - pWCStr);
            if ((pEndMBStr - pMBStr) < mbCount)
            {
                mbCount = (int)(pEndMBStr - pMBStr);
            }
            if (dwFlags & WC_NO_BEST_FIT_CHARS)
            {
                for (ctr = mbCount; ctr > 0; ctr--)
                {
                    GET_SB( pWC,
                            *pWCStr,
                            pMBStr );
                    ELIMINATE_BEST_FIT_SB( pHashN,
                                           *pWCStr,
                                           pMBStr );
                    pWCStr++;
                    pMBStr++;
                }
            }
            else
            {
                for (ctr = mbCount; ctr > 0; ctr--)
                {
                    GET_SB( pWC,
                            *pWCStr,
                            pMBStr );
                    pWCStr++;
                    pMBStr++;
                }
            }
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            if (dwFlags & WC_NO_BEST_FIT_CHARS)
            {
                while ((pWCStr < pEndWCStr) && (pMBStr < pEndMBStr))
                {
                    GET_MB( pWC,
                            *pWCStr,
                            pMBStr,
                            mbIncr,
                            ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                    ELIMINATE_BEST_FIT_MB( pHashN,
                                           *pWCStr,
                                           pMBStr,
                                           mbIncr,
                                           ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                    if (mbIncr == 0)
                    {
                        //
                        //  Not enough space in buffer.
                        //
                        break;
                    }

                    pWCStr++;
                    mbCount += mbIncr;
                    pMBStr += mbIncr;
                }
            }
            else
            {
                while ((pWCStr < pEndWCStr) && (pMBStr < pEndMBStr))
                {
                    GET_MB( pWC,
                            *pWCStr,
                            pMBStr,
                            mbIncr,
                            ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                    if (mbIncr == 0)
                    {
                        //
                        //  Not enough space in buffer.
                        //
                        break;
                    }

                    pWCStr++;
                    mbCount += mbIncr;
                    pMBStr += mbIncr;
                }
            }
        }

        //
        //  Make sure multibyte character buffer was large enough.
        //
        if (pWCStr < pEndWCStr)
        {
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return (0);
        }
    }

    //
    //  Return the number of characters written (or that would have
    //  been written) to the buffer.
    //
    return (mbCount);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetMBDefault
//
//  Translates the wide character string to a multibyte string and returns
//  the number of bytes written.  This also checks for the use of the default
//  character, so the translation is slower.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetMBDefault(
    PCP_HASH pHashN,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    LPBYTE pMBStr,
    int cbMultiByte,
    WORD wDefault,
    LPBOOL pUsedDef,
    DWORD dwFlags)
{
    int mbIncr;                   // amount to increment pMBStr
    int mbIncr2;                  // amount to increment pMBStr
    int mbCount = 0;              // count of multibyte chars written
    LPBYTE pEndMBStr;             // ptr to end of MB string buffer
    PWC_TABLE pWC = pHashN->pWC;  // ptr to WC table
    int ctr;                      // loop counter


    //
    //  If cbMultiByte is 0, then we can't use pMBStr.  In this
    //  case, we simply want to count the number of characters that
    //  would be written to the buffer.
    //
    if (cbMultiByte == 0)
    {
        BYTE pTempStr[2];             // tmp buffer - 2 bytes for DBCS

        //
        //  For each wide char, translate it to its corresponding multibyte
        //  char and increment the multibyte character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            mbCount = (int)(pEndWCStr - pWCStr);
            if (dwFlags & WC_NO_BEST_FIT_CHARS)
            {
                while (pWCStr < pEndWCStr)
                {
                    GET_SB( pWC,
                            *pWCStr,
                            pTempStr );
                    ELIMINATE_BEST_FIT_SB( pHashN,
                                           *pWCStr,
                                           pTempStr );
                    DEFAULT_CHAR_CHECK_SB( pHashN,
                                           *pWCStr,
                                           pTempStr,
                                           wDefault,
                                           pUsedDef );
                    pWCStr++;
                }
            }
            else
            {
                while (pWCStr < pEndWCStr)
                {
                    GET_SB( pWC,
                            *pWCStr,
                            pTempStr );
                    DEFAULT_CHAR_CHECK_SB( pHashN,
                                           *pWCStr,
                                           pTempStr,
                                           wDefault,
                                           pUsedDef );
                    pWCStr++;
                }
            }
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            if (dwFlags & WC_NO_BEST_FIT_CHARS)
            {
                while (pWCStr < pEndWCStr)
                {
                    GET_MB( pWC,
                            *pWCStr,
                            pTempStr,
                            mbIncr,
                            FALSE );
                    ELIMINATE_BEST_FIT_MB( pHashN,
                                           *pWCStr,
                                           pTempStr,
                                           mbIncr,
                                           FALSE );
                    DEFAULT_CHAR_CHECK_MB( pHashN,
                                           *pWCStr,
                                           pTempStr,
                                           wDefault,
                                           pUsedDef,
                                           mbIncr2,
                                           FALSE );
                    mbCount += (mbIncr2) ? (mbIncr2) : (mbIncr);
                    pWCStr++;
                }
            }
            else
            {
                while (pWCStr < pEndWCStr)
                {
                    GET_MB( pWC,
                            *pWCStr,
                            pTempStr,
                            mbIncr,
                            FALSE );
                    DEFAULT_CHAR_CHECK_MB( pHashN,
                                           *pWCStr,
                                           pTempStr,
                                           wDefault,
                                           pUsedDef,
                                           mbIncr2,
                                           FALSE );
                    mbCount += (mbIncr2) ? (mbIncr2) : (mbIncr);
                    pWCStr++;
                }
            }
        }
    }
    else
    {
        //
        //  Initialize multibyte loop pointers.
        //
        pEndMBStr = pMBStr + cbMultiByte;

        //
        //  For each wide char, translate it to its corresponding
        //  multibyte char, store it in pMBStr, and increment the
        //  multibyte character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            mbCount = (int)(pEndWCStr - pWCStr);
            if ((pEndMBStr - pMBStr) < mbCount)
            {
                mbCount = (int)(pEndMBStr - pMBStr);
            }
            if (dwFlags & WC_NO_BEST_FIT_CHARS)
            {
                for (ctr = mbCount; ctr > 0; ctr--)
                {
                    GET_SB( pWC,
                            *pWCStr,
                            pMBStr );
                    ELIMINATE_BEST_FIT_SB( pHashN,
                                           *pWCStr,
                                           pMBStr );
                    DEFAULT_CHAR_CHECK_SB( pHashN,
                                           *pWCStr,
                                           pMBStr,
                                           wDefault,
                                           pUsedDef );
                    pWCStr++;
                    pMBStr++;
                }
            }
            else
            {
                for (ctr = mbCount; ctr > 0; ctr--)
                {
                    GET_SB( pWC,
                            *pWCStr,
                            pMBStr );
                    DEFAULT_CHAR_CHECK_SB( pHashN,
                                           *pWCStr,
                                           pMBStr,
                                           wDefault,
                                           pUsedDef );
                    pWCStr++;
                    pMBStr++;
                }
            }
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            if (dwFlags & WC_NO_BEST_FIT_CHARS)
            {
                while ((pWCStr < pEndWCStr) && (pMBStr < pEndMBStr))
                {
                    GET_MB( pWC,
                            *pWCStr,
                            pMBStr,
                            mbIncr,
                            ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                    ELIMINATE_BEST_FIT_MB( pHashN,
                                           *pWCStr,
                                           pMBStr,
                                           mbIncr,
                                           ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                    DEFAULT_CHAR_CHECK_MB( pHashN,
                                           *pWCStr,
                                           pMBStr,
                                           wDefault,
                                           pUsedDef,
                                           mbIncr2,
                                           ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                    if ((mbIncr == 0) || (mbIncr2 == -1))
                    {
                        //
                        //  Not enough room in buffer.
                        //
                        break;
                    }

                    mbCount += (mbIncr2) ? (mbIncr2) : (mbIncr);
                    pWCStr++;
                    pMBStr += mbIncr;
                }
            }
            else
            {
                while ((pWCStr < pEndWCStr) && (pMBStr < pEndMBStr))
                {
                    GET_MB( pWC,
                            *pWCStr,
                            pMBStr,
                            mbIncr,
                            ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                    DEFAULT_CHAR_CHECK_MB( pHashN,
                                           *pWCStr,
                                           pMBStr,
                                           wDefault,
                                           pUsedDef,
                                           mbIncr2,
                                           ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                    if ((mbIncr == 0) || (mbIncr2 == -1))
                    {
                        //
                        //  Not enough room in buffer.
                        //
                        break;
                    }

                    mbCount += (mbIncr2) ? (mbIncr2) : (mbIncr);
                    pWCStr++;
                    pMBStr += mbIncr;
                }
            }
        }

        //
        //  Make sure multibyte character buffer was large enough.
        //
        if (pWCStr < pEndWCStr)
        {
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return (0);
        }
    }

    //
    //  Return the number of characters written (or that would have
    //  been written) to the buffer.
    //
    return (mbCount);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetMBDefaultComp
//
//  Translates the wide character string to a multibyte string and returns
//  the number of bytes written.  This also checks for the use of the default
//  character and tries to convert composite forms to precomposed forms, so
//  the translation is a lot slower.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetMBDefaultComp(
    PCP_HASH pHashN,
    LPWSTR pWCStr,
    LPWSTR pEndWCStr,
    LPBYTE pMBStr,
    int cbMultiByte,
    WORD wDefault,
    LPBOOL pUsedDef,
    DWORD dwFlags)
{
    int mbIncr;                   // amount to increment pMBStr
    int mbCount = 0;              // count of multibyte chars written
    LPBYTE pEndMBStr;             // ptr to end of MB string buffer
    BOOL fError;                  // if error during MB conversion


    //
    //  If cbMultiByte is 0, then we can't use pMBStr.  In this
    //  case, we simply want to count the number of characters that
    //  would be written to the buffer.
    //
    if (cbMultiByte == 0)
    {
        BYTE pTempStr[2];             // tmp buffer - 2 bytes for DBCS

        //
        //  Set most significant bit of flags to indicate to the
        //  GetMBComp routine that it's using a temporary storage
        //  area, so don't back up in the buffer.
        //
        SET_MSB(dwFlags);

        //
        //  For each wide char, translate it to its corresponding multibyte
        //  char and increment the multibyte character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            while (pWCStr < pEndWCStr)
            {
                //
                //  Get the translation.
                //
                mbCount += GetMBCompSB( pHashN,
                                        dwFlags,
                                        pWCStr,
                                        pTempStr,
                                        mbCount,
                                        wDefault,
                                        pUsedDef );
                pWCStr++;
            }
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            while (pWCStr < pEndWCStr)
            {
                //
                //  Get the translation.
                //
                mbCount += GetMBCompMB( pHashN,
                                        dwFlags,
                                        pWCStr,
                                        pTempStr,
                                        mbCount,
                                        wDefault,
                                        pUsedDef,
                                        &fError,
                                        FALSE );
                pWCStr++;
            }
        }
    }
    else
    {
        //
        //  Initialize multibyte loop pointers.
        //
        pEndMBStr = pMBStr + cbMultiByte;

        //
        //  For each wide char, translate it to its corresponding
        //  multibyte char, store it in pMBStr, and increment the
        //  multibyte character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            while ((pWCStr < pEndWCStr) && (pMBStr < pEndMBStr))
            {
                //
                //  Get the translation.
                //
                mbIncr = GetMBCompSB( pHashN,
                                      dwFlags,
                                      pWCStr,
                                      pMBStr,
                                      mbCount,
                                      wDefault,
                                      pUsedDef );
                pWCStr++;
                mbCount += mbIncr;
                pMBStr += mbIncr;
            }
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            while ((pWCStr < pEndWCStr) && (pMBStr < pEndMBStr))
            {
                //
                //  Get the translation.
                //
                mbIncr = GetMBCompMB( pHashN,
                                      dwFlags,
                                      pWCStr,
                                      pMBStr,
                                      mbCount,
                                      wDefault,
                                      pUsedDef,
                                      &fError,
                                      ((pMBStr + 1) < pEndMBStr) ? FALSE : TRUE );
                if (fError)
                {
                    //
                    //  Not enough room in the buffer.
                    //
                    break;
                }

                pWCStr++;
                mbCount += mbIncr;
                pMBStr += mbIncr;
            }
        }

        //
        //  Make sure multibyte character buffer was large enough.
        //
        if (pWCStr < pEndWCStr)
        {
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return (0);
        }
    }

    //
    //  Return the number of characters written (or that would have
    //  been written) to the buffer.
    //
    return (mbCount);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetMBCompSB
//
//  Fills in pMBStr with the byte character(s) for the corresponding wide
//  character from the appropriate translation table and returns the number
//  of byte characters written to pMBStr.  This routine is only called if
//  the defaultcheck and compositecheck flags were both set.
//
//  NOTE:  Most significant bit of dwFlags parameter is used by this routine
//         to indicate that the caller only wants the count of the number of
//         characters written, not the string (ie. do not back up in buffer).
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetMBCompSB(
    PCP_HASH pHashN,
    DWORD dwFlags,
    LPWSTR pWCStr,
    LPBYTE pMBStr,
    int mbCount,
    WORD wDefault,
    LPBOOL pUsedDef)
{
    WCHAR PreComp;                // precomposed wide character


    if ((pTblPtrs->pDefaultSortkey == NULL) ||
        (!IS_NONSPACE_ONLY(pTblPtrs->pDefaultSortkey, *pWCStr)))
    {
        //
        //  Get the 1:1 translation from wide char to single byte.
        //
        GET_WC_TRANSLATION_SB( pHashN,
                               *pWCStr,
                               pMBStr,
                               wDefault,
                               pUsedDef,
                               dwFlags );
        return (1);
    }
    else
    {
        if (mbCount < 1)
        {
            //
            //  Need to handle the nonspace character by itself, since
            //  it is the first character in the string.
            //
            if (dwFlags & WC_DISCARDNS)
            {
                //
                //  Discard the non-spacing char, so just return with
                //  zero chars written.
                //
                return (0);
            }
            else if (dwFlags & WC_DEFAULTCHAR)
            {
                //
                //  Need to replace the nonspace character with the default
                //  character and return the number of characters written
                //  to the multibyte string.
                //
                *pUsedDef = TRUE;
                *pMBStr = LOBYTE(wDefault);
                return (1);
            }
            else                  // WC_SEPCHARS - default
            {
                //
                //  Get the 1:1 translation from wide char to multibyte
                //  of the non-spacing char and return the number of
                //  characters written to the multibyte string.
                //
                GET_WC_TRANSLATION_SB( pHashN,
                                       *pWCStr,
                                       pMBStr,
                                       wDefault,
                                       pUsedDef,
                                       dwFlags );
                return (1);
            }
        }
        else if (PreComp = GetPreComposedChar(*pWCStr, *(pWCStr - 1)))
        {
            //
            //  Back up in the single byte string and write the
            //  precomposed char.
            //
            if (!IS_MSB(dwFlags))
            {
                pMBStr--;
            }

            GET_WC_TRANSLATION_SB( pHashN,
                                   PreComp,
                                   pMBStr,
                                   wDefault,
                                   pUsedDef,
                                   dwFlags );
            return (0);
        }
        else
        {
            if (dwFlags & WC_DISCARDNS)
            {
                //
                //  Discard the non-spacing char, so just return with
                //  zero chars written.
                //
                return (0);
            }
            else if (dwFlags & WC_DEFAULTCHAR)
            {
                //
                //  Need to replace the base character with the default
                //  character.  Since we've already written the base
                //  translation char in the single byte string, we need to
                //  back up in the single byte string and write the default
                //  char.
                //
                if (!IS_MSB(dwFlags))
                {
                    pMBStr--;
                }

                *pUsedDef = TRUE;
                *pMBStr = LOBYTE(wDefault);
                return (0);
            }
            else                  // WC_SEPCHARS - default
            {
                //
                //  Get the 1:1 translation from wide char to multibyte
                //  of the non-spacing char and return the number of
                //  characters written to the multibyte string.
                //
                GET_WC_TRANSLATION_SB( pHashN,
                                       *pWCStr,
                                       pMBStr,
                                       wDefault,
                                       pUsedDef,
                                       dwFlags );
                return (1);
            }
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  GetMBCompMB
//
//  Fills in pMBStr with the byte character(s) for the corresponding wide
//  character from the appropriate translation table and returns the number
//  of byte characters written to pMBStr.  This routine is only called if
//  the defaultcheck and compositecheck flags were both set.
//
//  If the buffer was too small, the fError flag will be set to TRUE.
//
//  NOTE:  Most significant bit of dwFlags parameter is used by this routine
//         to indicate that the caller only wants the count of the number of
//         characters written, not the string (ie. do not back up in buffer).
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetMBCompMB(
    PCP_HASH pHashN,
    DWORD dwFlags,
    LPWSTR pWCStr,
    LPBYTE pMBStr,
    int mbCount,
    WORD wDefault,
    LPBOOL pUsedDef,
    BOOL *fError,
    BOOL fOnlyOne)
{
    WCHAR PreComp;                // precomposed wide character
    BYTE pTmpSp[2];               // temp space - 2 bytes for DBCS
    int nCnt;                     // number of characters written


    *fError = FALSE;
    if ((pTblPtrs->pDefaultSortkey == NULL) ||
        (!IS_NONSPACE_ONLY(pTblPtrs->pDefaultSortkey, *pWCStr)))
    {
        //
        //  Get the 1:1 translation from wide char to multibyte.
        //  This also handles DBCS and returns the number of characters
        //  written to the multibyte string.
        //
        GET_WC_TRANSLATION_MB( pHashN,
                               *pWCStr,
                               pMBStr,
                               wDefault,
                               pUsedDef,
                               nCnt,
                               fOnlyOne,
                               dwFlags );
        if (nCnt == 0)
        {
            *fError = TRUE;
        }
        return (nCnt);
    }
    else
    {
        if (mbCount < 1)
        {
            //
            //  Need to handle the nonspace character by itself, since
            //  it is the first character in the string.
            //
            if (dwFlags & WC_DISCARDNS)
            {
                //
                //  Discard the non-spacing char, so just return with
                //  zero chars written.
                //
                return (0);
            }
            else if (dwFlags & WC_DEFAULTCHAR)
            {
                //
                //  Need to replace the nonspace character with the default
                //  character and return the number of characters written
                //  to the multibyte string.
                //
                *pUsedDef = TRUE;
                COPY_MB_CHAR( wDefault,
                              pMBStr,
                              nCnt,
                              fOnlyOne );
                if (nCnt == 0)
                {
                    *fError = TRUE;
                }
                return (nCnt);
            }
            else                  // WC_SEPCHARS - default
            {
                //
                //  Get the 1:1 translation from wide char to multibyte
                //  of the non-spacing char and return the number of
                //  characters written to the multibyte string.
                //
                GET_WC_TRANSLATION_MB( pHashN,
                                       *pWCStr,
                                       pMBStr,
                                       wDefault,
                                       pUsedDef,
                                       nCnt,
                                       fOnlyOne,
                                       dwFlags );
                if (nCnt == 0)
                {
                    *fError = TRUE;
                }
                return (nCnt);
            }

        }
        else if (PreComp = GetPreComposedChar(*pWCStr, *(pWCStr - 1)))
        {
            //
            //  Get the 1:1 translation from wide char to multibyte
            //  of the precomposed char, back up in the multibyte string,
            //  write the precomposed char, and return the DIFFERENCE of
            //  the number of characters written to the the multibyte
            //  string.
            //
            GET_WC_TRANSLATION_MB( pHashN,
                                   *(pWCStr - 1),
                                   pTmpSp,
                                   wDefault,
                                   pUsedDef,
                                   nCnt,
                                   fOnlyOne,
                                   dwFlags );
            if (nCnt == 0)
            {
                *fError = TRUE;
                return (nCnt);
            }

            if (!IS_MSB(dwFlags))
            {
                pMBStr -= nCnt;
            }

            GET_WC_TRANSLATION_MB( pHashN,
                                   PreComp,
                                   pMBStr,
                                   wDefault,
                                   pUsedDef,
                                   mbCount,
                                   fOnlyOne,
                                   dwFlags );
            if (mbCount == 0)
            {
                *fError = TRUE;
            }
            return (mbCount - nCnt);
        }
        else
        {
            if (dwFlags & WC_DISCARDNS)
            {
                //
                //  Discard the non-spacing char, so just return with
                //  zero chars written.
                //
                return (0);
            }
            else if (dwFlags & WC_DEFAULTCHAR)
            {
                //
                //  Need to replace the base character with the default
                //  character.  Since we've already written the base
                //  translation char in the multibyte string, we need to
                //  back up in the multibyte string and return the
                //  DIFFERENCE of the number of characters written
                //  (could be negative).
                //

                //
                //  If the previous character written is the default
                //  character, then the base character for this nonspace
                //  character has already been replaced.  Simply throw
                //  this character away and return zero chars written.
                //
                if (!IS_MSB(dwFlags))
                {
                    //
                    //  Not using a temporary buffer, so find out if the
                    //  previous character translated was the default char.
                    //
                    if ((MAKEWORD(*(pMBStr - 1), 0) == wDefault) ||
                        ((mbCount > 1) &&
                         (MAKEWORD(*(pMBStr - 1), *(pMBStr - 2)) == wDefault)))
                    {
                        return (0);
                    }
                }
                else
                {
                    //
                    //  Using a temporary buffer.  The temp buffer is 2 bytes
                    //  in length and contains the previous character written.
                    //
                    if ((MAKEWORD(*pMBStr, 0) == wDefault) ||
                        ((mbCount > 1) &&
                         (MAKEWORD(*pMBStr, *(pMBStr + 1)) == wDefault)))
                    {
                        return (0);
                    }
                }

                //
                //  Get the 1:1 translation from wide char to multibyte
                //  of the base char, back up in the multibyte string,
                //  write the default char, and return the DIFFERENCE of
                //  the number of characters written to the the multibyte
                //  string.
                //
                GET_WC_TRANSLATION_MB( pHashN,
                                       *(pWCStr - 1),
                                       pTmpSp,
                                       wDefault,
                                       pUsedDef,
                                       nCnt,
                                       fOnlyOne,
                                       dwFlags );
                if (nCnt == 0)
                {
                    *fError = TRUE;
                    return (nCnt);
                }

                if (!IS_MSB(dwFlags))
                {
                    pMBStr -= nCnt;
                }

                *pUsedDef = TRUE;
                COPY_MB_CHAR( wDefault,
                              pMBStr,
                              mbCount,
                              fOnlyOne );
                if (mbCount == 0)
                {
                    *fError = TRUE;
                }
                return (mbCount - nCnt);
            }
            else                  // WC_SEPCHARS - default
            {
                //
                //  Get the 1:1 translation from wide char to multibyte
                //  of the non-spacing char and return the number of
                //  characters written to the multibyte string.
                //
                GET_WC_TRANSLATION_MB( pHashN,
                                       *pWCStr,
                                       pMBStr,
                                       wDefault,
                                       pUsedDef,
                                       nCnt,
                                       fOnlyOne,
                                       dwFlags );
                if (nCnt == 0)
                {
                    *fError = TRUE;
                }
                return (nCnt);
            }
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  GetMacCodePage
//
//  Returns the system default Mac code page.
//
//  09-22-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

UINT GetMacCodePage()
{
    PKEY_VALUE_FULL_INFORMATION pKeyValueFull;   // ptr to query information
    BYTE pStatic[MAX_KEY_VALUE_FULLINFO];        // ptr to static buffer
    UNICODE_STRING ObUnicodeStr;                 // unicode string
    UINT CodePage;                               // code page value
    PCP_HASH pHashN;                             // ptr to hash node


    //
    //  See if the Mac code page globals have been initialized yet.
    //  If they have, return the mac code page value.
    //
    if (gMacCodePage != 0)
    {
        return (gMacCodePage);
    }

    //
    //  Make sure code page key is open.
    //
    OPEN_CODEPAGE_KEY(NLS_DEFAULT_MACCP);

    //
    //  Query the registry for the Mac CP value.
    //
    CodePage = 0;
    pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
    if ((QueryRegValue( hCodePageKey,
                        NLS_VALUE_MACCP,
                        &pKeyValueFull,
                        MAX_KEY_VALUE_FULLINFO,
                        NULL )) == NO_ERROR)
    {
        //
        //  Convert the value to an integer.
        //
        RtlInitUnicodeString(&ObUnicodeStr, GET_VALUE_DATA_PTR(pKeyValueFull));
        if (RtlUnicodeStringToInteger(&ObUnicodeStr, 10, (PULONG)&CodePage))
        {
            CodePage = 0;
        }
    }

    //
    //  Make sure the CodePage value was set.
    //
    if (CodePage == 0)
    {
        //
        //  Registry value is corrupt, so use default Mac code page.
        //
        CodePage = NLS_DEFAULT_MACCP;
    }

    //
    //  Get the hash node for the Mac code page.
    //
    pHashN = GetCPHashNode(CodePage);

    //
    //  Make sure the Mac hash node is valid.
    //
    if (pHashN == NULL)
    {
        //
        //  Invalid hash node, which means either the registry is
        //  corrupt, or setup failed to install a file.  Use the
        //  Ansi code page values.
        //
        CodePage = gAnsiCodePage;
        pHashN = gpACPHashN;
    }

    //
    //  Set the final MAC CP values.
    //
    RtlEnterCriticalSection(&gcsTblPtrs);

    if (gMacCodePage == 0)
    {
        gpMACCPHashN = pHashN;
        gMacCodePage = CodePage;
    }

    RtlLeaveCriticalSection(&gcsTblPtrs);

    //
    //  Return the Mac code page value.
    //
    return (gMacCodePage);
}


////////////////////////////////////////////////////////////////////////////
//
//  SpecialMBToWC
//
//  Maps a multibyte character string to its wide character string
//  counterpart.
//
//  08-21-95    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int SpecialMBToWC(
    PCP_HASH pHashN,
    DWORD dwFlags,
    LPCSTR lpMultiByteStr,
    int cbMultiByte,
    LPWSTR lpWideCharStr,
    int cchWideChar)
{
    register LPBYTE pMBStr;       // ptr to search through MB string
    register LPWSTR pWCStr;       // ptr to search through WC string
    LPBYTE pEndMBStr;             // ptr to end of MB search string
    LPWSTR pEndWCStr;             // ptr to end of WC string buffer
    int mbIncr;                   // amount to increment pMBStr
    int wcCount = 0;              // count of wide chars written
    PMB_TABLE pMBTbl;             // ptr to MB table
    int ctr;                      // loop counter


    //
    //  Initialize multibyte character loop pointers.
    //
    pMBStr = (LPBYTE)lpMultiByteStr;
    pEndMBStr = pMBStr + cbMultiByte;

    //
    //  Get the MB table.
    //
    pMBTbl = pHashN->pMBTbl;

    //
    //  If cchWideChar is 0, then we can't use lpWideCharStr.  In this
    //  case, we simply want to count the number of characters that would
    //  be written to the buffer.
    //
    if (cchWideChar == 0)
    {
        //
        //  For each multibyte char, translate it to its corresponding
        //  wide char and increment the wide character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            wcCount = (int)(pEndMBStr - pMBStr);
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            WCHAR pTempStr[MAX_COMPOSITE];   // tmp buffer

            pEndWCStr = pTempStr + MAX_COMPOSITE;
            while (pMBStr < pEndMBStr)
            {
                GET_WC_MULTI( pHashN,
                              pMBTbl,
                              pMBStr,
                              pEndMBStr,
                              pTempStr,
                              pEndWCStr,
                              mbIncr );
                pMBStr += mbIncr;
                wcCount++;
            }
        }
    }
    else
    {
        //
        //  Initialize wide character loop pointers.
        //
        pWCStr = lpWideCharStr;
        pEndWCStr = pWCStr + cchWideChar;

        //
        //  For each multibyte char, translate it to its corresponding
        //  wide char, store it in lpWideCharStr, and increment the wide
        //  character count.
        //
        if (IS_SBCS_CP(pHashN))
        {
            //
            //  Single Byte Character Code Page.
            //
            wcCount = (int)(pEndMBStr - pMBStr);
            if ((pEndWCStr - pWCStr) < wcCount)
            {
                wcCount = (int)(pEndWCStr - pWCStr);
            }

            if (dwFlags & MB_INVALID_CHAR_CHECK)
            {
                //
                //  Error check flag is set.
                //
                for (ctr = wcCount; ctr > 0; ctr--)
                {
                    GET_WC_SINGLE_SPECIAL( pHashN,
                                           pMBTbl,
                                           pMBStr,
                                           pWCStr );
                    pMBStr++;
                    pWCStr++;
                }
            }
            else
            {
                //
                //  Error check flag is NOT set.
                //
                for (ctr = wcCount; ctr > 0; ctr--)
                {
                    GET_WC_SINGLE( pMBTbl,
                                   pMBStr,
                                   pWCStr );
                    pMBStr++;
                    pWCStr++;
                }
            }
        }
        else
        {
            //
            //  Multi Byte Character Code Page.
            //
            if (dwFlags & MB_INVALID_CHAR_CHECK)
            {
                //
                //  Error check flag is set.
                //
                while ((pMBStr < pEndMBStr) && (pWCStr < pEndWCStr))
                {
                    GET_WC_MULTI_ERR_SPECIAL( pHashN,
                                              pMBTbl,
                                              pMBStr,
                                              pEndMBStr,
                                              pWCStr,
                                              pEndWCStr,
                                              mbIncr );
                    pMBStr += mbIncr;
                    pWCStr++;
                }
                wcCount = (int)(pWCStr - lpWideCharStr);
            }
            else
            {
                //
                //  Error check flag is NOT set.
                //
                while ((pMBStr < pEndMBStr) && (pWCStr < pEndWCStr))
                {
                    GET_WC_MULTI( pHashN,
                                  pMBTbl,
                                  pMBStr,
                                  pEndMBStr,
                                  pWCStr,
                                  pEndWCStr,
                                  mbIncr );
                    pMBStr += mbIncr;
                    pWCStr++;
                }
                wcCount = (int)(pWCStr - lpWideCharStr);
            }
        }

        //
        //  Make sure wide character buffer was large enough.
        //
        if (pMBStr < pEndMBStr)
        {
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return (0);
        }
    }

    //
    //  Return the number of characters written (or that would have
    //  been written) to the buffer.
    //
    return (wcCount);
}