/*++

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

Module Name:

    nls.h

Abstract:

    This file contains the header information shared by all of the modules
    of NLS.

Revision History:

    05-31-91    JulieB    Created.
    03-07-00    lguindon  Began Geo API port

--*/



#ifndef _NLS_
#define _NLS_




////////////////////////////////////////////////////////////////////////////
//
//  RTL Includes Files.
//
////////////////////////////////////////////////////////////////////////////

#ifndef RC_INVOKED
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#endif



////////////////////////////////////////////////////////////////////////////
//
//  Include Files.
//
////////////////////////////////////////////////////////////////////////////

#include <base.h>
#include <ntcsrdll.h>
#include <ntcsrsrv.h>
#include <basemsg.h>
#include <windows.h>
#include <winnlsp.h>
#include <winerror.h>
#include <string.h>
#include <stdlib.h>





////////////////////////////////////////////////////////////////////////////
//
//  Constant Declarations.
//
////////////////////////////////////////////////////////////////////////////

//
//  Code Page Ranges.
//
#define NLS_CP_TABLE_RANGE        0         // begin code page Table range
#define NLS_CP_DLL_RANGE          50000     // begin code page DLL range
#define NLS_CP_ALGORITHM_RANGE    60000     // begin code page Algorithm range


//
//  Table Values.
//
#define MB_TBL_SIZE               256  // size of MB tables
#define GLYPH_TBL_SIZE            256  // size of GLYPH tables
#define DBCS_TBL_SIZE             256  // size of DBCS tables

#define CP_TBL_SIZE               197  // size of code page hash table (prime #)
#define LOC_TBL_SIZE              197  // size of locale hash table (prime #)


//
//  String Constants.
//
#define MAX_PATH_LEN              512  // max length of path name
#define MAX_STRING_LEN            128  // max string length for static buffer
#define MAX_SMALL_BUF_LEN         64   // max length of small buffer

#define MAX_COMPOSITE             5    // max number of composite characters
#define MAX_EXPANSION             3    // max number of expansion characters
#define MAX_TBL_EXPANSION         2    // max expansion chars per table entry
#define MAX_WEIGHTS               9    // max number of words in all weights

#define MAX_SECURITY_BUF_LEN      128  // max length of security descriptor buffer

// length of sortkey static buffer
#define MAX_SORTKEY_BUF_LEN       ( MAX_SMALL_BUF_LEN * MAX_EXPANSION * MAX_WEIGHTS )


#define MAX_FONTSIGNATURE         16   // length of font signature string

// SetLocaleInfo string constants
#define MAX_SLIST                 4    // max wide chars in sList
#define MAX_IMEASURE              2    // max wide chars in iMeasure
#define MAX_SDECIMAL              4    // max wide chars in sDecimal
#define MAX_STHOUSAND             4    // max wide chars in sThousand
#define MAX_SGROUPING             10   // max wide chars in sGrouping
#define MAX_IDIGITS               2    // max wide chars in iDigits
#define MAX_ILZERO                2    // max wide chars in iLZero
#define MAX_INEGNUMBER            2    // max wide chars in iNegNumber
#define MAX_SNATIVEDIGITS         11   // max wide chars in sNativeDigits
#define MAX_IDIGITSUBST           2    // max wide chars in iDigitSubstitution
#define MAX_SCURRENCY             6    // max wide chars in sCurrency
#define MAX_SMONDECSEP            4    // max wide chars in sMonDecimalSep
#define MAX_SMONTHOUSEP           4    // max wide chars in sMonThousandSep
#define MAX_SMONGROUPING          10   // max wide chars in sMonGrouping
#define MAX_ICURRENCY             2    // max wide chars in iCurrency
#define MAX_SPOSSIGN              5    // max wide chars in sPositiveSign
#define MAX_SNEGSIGN              5    // max wide chars in sNegativeSign
#define MAX_STIMEFORMAT           MAX_REG_VAL_SIZE   // max wide chars in sTimeFormat
#define MAX_STIME                 4    // max wide chars in sTime
#define MAX_ITIME                 2    // max wide chars in iTime
#define MAX_S1159                 15   // max wide chars in s1159
#define MAX_S2359                 15   // max wide chars in s2359
#define MAX_SSHORTDATE            MAX_REG_VAL_SIZE   // max wide chars in sShortDate
#define MAX_SDATE                 4    // max wide chars in sDate
#define MAX_SYEARMONTH            MAX_REG_VAL_SIZE   // max wide chars in sYearMonth
#define MAX_SLONGDATE             MAX_REG_VAL_SIZE   // max wide chars in sLongDate
#define MAX_ICALTYPE              3    // max wide chars in iCalendarType
#define MAX_IFIRSTDAY             2    // max wide chars in iFirstDayOfWeek
#define MAX_IFIRSTWEEK            2    // max wide chars in iFirstWeekOfYear

//
//  NOTE:  If any of the MAX_VALUE_ values change, then the corresponding
//         MAX_CHAR_ value must also change.
//
#define MAX_VALUE_IMEASURE        1    // max value for iMeasure
#define MAX_VALUE_IDIGITS         9    // max value for iDigits
#define MAX_VALUE_ILZERO          1    // max value for iLZero
#define MAX_VALUE_INEGNUMBER      4    // max value for iNegNumber
#define MAX_VALUE_IDIGITSUBST     2    // max value for iDigitSubstitution
#define MAX_VALUE_ICURRDIGITS     99   // max value for iCurrDigits
#define MAX_VALUE_ICURRENCY       3    // max value for iCurrency
#define MAX_VALUE_INEGCURR        15   // max value for iNegCurr
#define MAX_VALUE_ITIME           1    // max value for iTime
#define MAX_VALUE_IFIRSTDAY       6    // max value for iFirstDayOfWeek
#define MAX_VALUE_IFIRSTWEEK      2    // max value for iFirstWeekOfYear

#define MAX_CHAR_IMEASURE       L'1'   // max char value for iMeasure
#define MAX_CHAR_IDIGITS        L'9'   // max char value for iDigits
#define MAX_CHAR_ILZERO         L'1'   // max char value for iLZero
#define MAX_CHAR_INEGNUMBER     L'4'   // max char value for iNegNumber
#define MAX_CHAR_IDIGITSUBST    L'2'   // max char value for iDigitSubstitution
#define MAX_CHAR_ICURRENCY      L'3'   // max char value for iCurrency
#define MAX_CHAR_ITIME          L'1'   // max char value for iTime
#define MAX_CHAR_IFIRSTDAY      L'6'   // max char value for iFirstDayOfWeek
#define MAX_CHAR_IFIRSTWEEK     L'2'   // max char value for iFirstWeekOfYear


// SetCalendarInfo string constants
#define MAX_ITWODIGITYEAR         5    // max wide chars in TwoDigitYearMax


#define NLS_CHAR_ZERO           L'0'   // digit 0 character
#define NLS_CHAR_ONE            L'1'   // digit 1 character
#define NLS_CHAR_NINE           L'9'   // digit 9 character
#define NLS_CHAR_SEMICOLON      L';'   // semicolon character
#define NLS_CHAR_PERIOD         L'.'   // period character
#define NLS_CHAR_QUOTE          L'\''  // single quote character
#define NLS_CHAR_SPACE          L' '   // space character
#define NLS_CHAR_HYPHEN         L'-'   // hyphen/minus character
#define NLS_CHAR_OPEN_PAREN     L'('   // open parenthesis character
#define NLS_CHAR_CLOSE_PAREN    L')'   // close parenthesis character

#define MAX_BLANKS                1    // max successive blanks in number string


//
//  RC File Constants.
//
#define NLS_SORT_RES_PREFIX       L"SORT_"
#define NLS_SORT_RES_DEFAULT      L"SORT_00000000"


//
//  Size of stack buffer for PKEY_VALUE_FULL_INFORMATION pointer.
//
#define MAX_KEY_VALUE_FULLINFO                                             \
    ( FIELD_OFFSET( KEY_VALUE_FULL_INFORMATION, Name ) + MAX_PATH_LEN )


//
//  Paths to registry keys.
//
#define NLS_HKLM_SYSTEM    L"\\Registry\\Machine\\System\\CurrentControlSet\\Control"
#define NLS_HKLM_SOFTWARE  L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion"


//
//  Names of Registry Key Entries.
//
#define NLS_CODEPAGE_KEY           L"\\Nls\\Codepage"
#define NLS_LANGUAGE_GROUPS_KEY    L"\\Nls\\Language Groups"
#define NLS_LOCALE_KEY             L"\\Nls\\Locale"
#define NLS_ALT_SORTS_KEY          L"\\Nls\\Locale\\Alternate Sorts"
#define NLS_MUILANG_KEY            L"\\Nls\\MUILanguages"

//
//  User Info.
//
#define NLS_CTRL_PANEL_KEY         L"Control Panel\\International"
#define NLS_CALENDARS_KEY          L"Control Panel\\International\\Calendars"
#define NLS_TWO_DIGIT_YEAR_KEY     L"Control Panel\\International\\Calendars\\TwoDigitYearMax"
#define NLS_POLICY_TWO_DIGIT_YEAR_KEY L"Software\\Policies\\Microsoft\\Control Panel\\International\\Calendars\\TwoDigitYearMax"

//
//  GEO Registry Keys.
//
#define GEO_REG_KEY         L"Control Panel\\International\\Geo"
#define GEO_REG_NATION      L"Nation"
#define GEO_REG_REGION      L"Region"
#define GEO_REG_STATE       L"State"
#define GEO_REG_CITY        L"City"

//
//  Name of NLS Object Directory.
//  Must create this in order to have "create access" on the fly.
//
#define NLS_OBJECT_DIRECTORY_NAME  L"\\NLS"

//
//  Default values.
//
#define NLS_DEFAULT_ACP           1252
#define NLS_DEFAULT_OEMCP         437
#define NLS_DEFAULT_MACCP         10000
#define NLS_DEFAULT_LANGID        0x0409
#define NLS_DEFAULT_UILANG        0x0409


//
//  DLL Translation Function Names.
//
//  ****  Must be an ANSI string for GetProcAddress call.  ****
//
#define NLS_CP_DLL_PROC_NAME      "NlsDllCodePageTranslation"


//
//  Flag Constants.
//
#define MSB_FLAG         0x80000000    // most significant bit set


//
//  Table Header Constants  (all sizes in WORDS).
//
#define CP_HEADER                 1    // size of CP Info table header
#define MB_HEADER                 1    // size of MB table header
#define GLYPH_HEADER              1    // size of GLYPH table header
#define DBCS_HEADER               1    // size of DBCS table header
#define WC_HEADER                 1    // size of WC table header
#define CT_HEADER                 2    // size of CTYPE table header
#define LANG_HDR_OFFSET           0    // offset to LANGUAGE file header
#define LANG_HEADER               1    // size of language file header
#define UP_HEADER                 1    // size of UPPERCASE table header
#define LO_HEADER                 1    // size of LOWERCASE table header
#define L_EXCEPT_HDR_OFFSET       2    // offset to LANGUAGE EXCEPTION header
#define AD_HEADER                 1    // size of ASCIIDIGITS table header
#define CZ_HEADER                 1    // size of FOLDCZONE table header
#define HG_HEADER                 1    // size of HIRAGANA table header
#define KK_HEADER                 1    // size of KATAKANA table header
#define HW_HEADER                 1    // size of HALF WIDTH table header
#define FW_HEADER                 1    // size of FULL WIDTH table header
#define TR_HEADER                 1    // size of TRADITIONAL CHINESE table header
#define SP_HEADER                 1    // size of SIMPLIFIED CHINESE table header
#define PC_HEADER                 1    // size of PRECOMPOSED table header
#define CO_HEADER                 3    // size of COMPOSITE table header
#define SORTKEY_HEADER            2    // size of SORTKEY table header
#define REV_DW_HEADER             2    // size of REVERSE DW table header
#define DBL_COMP_HEADER           2    // size of DOUBLE COMPRESS table header
#define IDEO_LCID_HEADER          2    // size of IDEOGRAPH LCID table header
#define EXPAND_HEADER             2    // size of EXPANSION table header
#define COMPRESS_HDR_OFFSET       2    // offset to COMPRESSION header
#define EXCEPT_HDR_OFFSET         2    // offset to EXCEPTION header
#define MULTI_WT_HEADER           1    // size of MULTIPLE WEIGHTS table header
#define JAMO_INDEX_HEADER         1    // size of Jamo Index table header
#define JAMO_COMPOSITION_HEADER   1    // size of Jamo Composition state machine table hader


//
//  Invalid Flag Checks.
//
#define MB_INVALID_FLAG   (~(MB_PRECOMPOSED   | MB_COMPOSITE |              \
                             MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
#define WC_INVALID_FLAG   (~(WC_NO_BEST_FIT_CHARS | WC_COMPOSITECHECK |     \
                             WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR))
#define CS_INVALID_FLAG   (~(NORM_IGNORECASE    | NORM_IGNORENONSPACE |     \
                             NORM_IGNORESYMBOLS | NORM_IGNOREKANATYPE |     \
                             NORM_IGNOREWIDTH   | SORT_STRINGSORT     |     \
                             LOCALE_USE_CP_ACP  | NORM_STOP_ON_NULL))
#define FS_INVALID_FLAG   (~(MAP_FOLDCZONE | MAP_PRECOMPOSED |              \
                             MAP_COMPOSITE | MAP_FOLDDIGITS))
#define LCMS_INVALID_FLAG (~(LCMAP_LOWERCASE | LCMAP_UPPERCASE |            \
                             LCMAP_LINGUISTIC_CASING |                      \
                             LCMAP_SORTKEY   | LCMAP_BYTEREV   |            \
                             LCMAP_HIRAGANA  | LCMAP_KATAKANA  |            \
                             LCMAP_HALFWIDTH | LCMAP_FULLWIDTH |            \
                             LCMAP_TRADITIONAL_CHINESE |                    \
                             LCMAP_SIMPLIFIED_CHINESE  |                    \
                             NORM_IGNORECASE    | NORM_IGNORENONSPACE |     \
                             NORM_IGNORESYMBOLS | NORM_IGNOREKANATYPE |     \
                             NORM_IGNOREWIDTH   | SORT_STRINGSORT     |     \
                             LOCALE_USE_CP_ACP))
#define GTF_INVALID_FLAG  (~(LOCALE_NOUSEROVERRIDE | LOCALE_USE_CP_ACP |    \
                             TIME_NOMINUTESORSECONDS                   |    \
                             TIME_NOSECONDS | TIME_NOTIMEMARKER        |    \
                             TIME_FORCE24HOURFORMAT))
#define GDF_INVALID_FLAG  (~(LOCALE_NOUSEROVERRIDE | LOCALE_USE_CP_ACP |    \
                             DATE_LTRREADING       | DATE_RTLREADING   |    \
                             DATE_SHORTDATE        | DATE_LONGDATE     |    \
                             DATE_YEARMONTH        | DATE_USE_ALT_CALENDAR |\
                             DATE_ADDHIJRIDATETEMP))
#define IVLG_INVALID_FLAG (~(LGRPID_INSTALLED | LGRPID_SUPPORTED))
#define IVL_INVALID_FLAG  (~(LCID_INSTALLED | LCID_SUPPORTED))
#define GNF_INVALID_FLAG  (~(LOCALE_NOUSEROVERRIDE | LOCALE_USE_CP_ACP))
#define GCF_INVALID_FLAG  (~(LOCALE_NOUSEROVERRIDE | LOCALE_USE_CP_ACP))
#define ESLG_INVALID_FLAG (~(LGRPID_INSTALLED | LGRPID_SUPPORTED))
#define ESL_INVALID_FLAG  (~(LCID_INSTALLED | LCID_SUPPORTED |             \
                             LCID_ALTERNATE_SORTS))
#define ESCP_INVALID_FLAG (~(CP_INSTALLED | CP_SUPPORTED))
#define ETF_INVALID_FLAG  (~(LOCALE_USE_CP_ACP))


//
//  Single Flags (only one at a time is valid).
//
#define LCMS1_SINGLE_FLAG (LCMAP_LOWERCASE | LCMAP_UPPERCASE |             \
                           LCMAP_SORTKEY)
#define LCMS2_SINGLE_FLAG (LCMAP_HIRAGANA  | LCMAP_KATAKANA  |             \
                           LCMAP_SORTKEY)
#define LCMS3_SINGLE_FLAG (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH |             \
                           LCMAP_SORTKEY)
#define LCMS4_SINGLE_FLAG (LCMAP_TRADITIONAL_CHINESE |                     \
                           LCMAP_SIMPLIFIED_CHINESE  |                     \
                           LCMAP_SORTKEY)
#define GDF_SINGLE_FLAG   (DATE_LTRREADING | DATE_RTLREADING)
#define IVLG_SINGLE_FLAG  (LGRPID_INSTALLED  | LGRPID_SUPPORTED)
#define IVL_SINGLE_FLAG   (LCID_INSTALLED  | LCID_SUPPORTED)
#define ESLG_SINGLE_FLAG  (LGRPID_INSTALLED  | LGRPID_SUPPORTED)
#define ESL_SINGLE_FLAG   (LCID_INSTALLED  | LCID_SUPPORTED)
#define ESCP_SINGLE_FLAG  (CP_INSTALLED    | CP_SUPPORTED)


//
//  Flag combinations.
//
#define WC_COMPCHK_FLAGS  (WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR)
#define NORM_ALL          (NORM_IGNORECASE    | NORM_IGNORENONSPACE |      \
                           NORM_IGNORESYMBOLS | NORM_IGNOREKANATYPE |      \
                           NORM_IGNOREWIDTH)
#define NORM_SORTKEY_ONLY (NORM_IGNORECASE    | NORM_IGNOREKANATYPE |      \
                           NORM_IGNOREWIDTH   | SORT_STRINGSORT)
#define NORM_ALL_CASE     (NORM_IGNORECASE    | NORM_IGNOREKANATYPE |      \
                           NORM_IGNOREWIDTH)
#define LCMAP_NO_NORM     (LCMAP_LOWERCASE    | LCMAP_UPPERCASE     |      \
                           LCMAP_HIRAGANA     | LCMAP_KATAKANA      |      \
                           LCMAP_HALFWIDTH    | LCMAP_FULLWIDTH     |      \
                           LCMAP_TRADITIONAL_CHINESE                |      \
                           LCMAP_SIMPLIFIED_CHINESE)

//
//  Get the LCType value from an LCType.
//
#define NLS_GET_LCTYPE_VALUE(x)  (x & ~(LOCALE_NOUSEROVERRIDE |  \
                                        LOCALE_USE_CP_ACP     |  \
                                        LOCALE_RETURN_NUMBER))

//
//  Get the CalType value from a CalType.
//
#define NLS_GET_CALTYPE_VALUE(x) (x & ~(CAL_NOUSEROVERRIDE |  \
                                        CAL_USE_CP_ACP     |  \
                                        CAL_RETURN_NUMBER))

//
//  Separator and Terminator Values - Sortkey String.
//
#define SORTKEY_SEPARATOR    0x01
#define SORTKEY_TERMINATOR   0x00


//
//  Lowest weight values.
//  Used to remove trailing DW and CW values.
//
#define MIN_DW  2
#define MIN_CW  2


//
//  Bit mask values.
//
//  Case Weight (CW) - 8 bits:
//    bit 0   => width
//    bit 1,2 => small kana, sei-on
//    bit 3,4 => upper/lower case
//    bit 5   => kana
//    bit 6,7 => compression
//
#define COMPRESS_3_MASK      0xc0      // compress 3-to-1 or 2-to-1
#define COMPRESS_2_MASK      0x80      // compress 2-to-1

#define CASE_MASK            0x3f      // zero out compression bits

#define CASE_UPPER_MASK      0xe7      // zero out case bits
#define CASE_SMALL_MASK      0xf9      // zero out small modifier bits
#define CASE_KANA_MASK       0xdf      // zero out kana bit
#define CASE_WIDTH_MASK      0xfe      // zero out width bit

#define SW_POSITION_MASK     0x8003    // avoid 0 or 1 in bytes of word

//
//  Bit Mask Values for CompareString.
//
//  NOTE: Due to intel byte reversal, the DWORD value is backwards:
//                CW   DW   SM   AW
//
//  Case Weight (CW) - 8 bits:
//    bit 0   => width
//    bit 4   => case
//    bit 5   => kana
//    bit 6,7 => compression
//
#define CMP_MASKOFF_NONE          0xffffffff
#define CMP_MASKOFF_DW            0xff00ffff
#define CMP_MASKOFF_CW            0xe7ffffff
#define CMP_MASKOFF_DW_CW         0xe700ffff
#define CMP_MASKOFF_COMPRESSION   0x3fffffff

#define CMP_MASKOFF_KANA          0xdfffffff
#define CMP_MASKOFF_WIDTH         0xfeffffff
#define CMP_MASKOFF_KANA_WIDTH    0xdeffffff

//
//  Masks to isolate the various bits in the case weight.
//
//  NOTE: Bit 2 must always equal 1 to avoid getting a byte value
//        of either 0 or 1.
//
#define CASE_XW_MASK         0xc4

#define ISOLATE_SMALL        ( (BYTE)((~CASE_SMALL_MASK) | CASE_XW_MASK) )
#define ISOLATE_KANA         ( (BYTE)((~CASE_KANA_MASK)  | CASE_XW_MASK) )
#define ISOLATE_WIDTH        ( (BYTE)((~CASE_WIDTH_MASK) | CASE_XW_MASK) )

//
//  UW Mask for Cho-On:
//    Leaves bit 7 on in AW, so it becomes Repeat if it follows Kana N.
//
#define CHO_ON_UW_MASK       0xff87

//
//  Values for fareast special case alphanumeric weights.
//
#define AW_REPEAT            0
#define AW_CHO_ON            1
#define MAX_SPECIAL_AW       AW_CHO_ON

//
//  Values for weight 5 - East Asia Extra Weights.
//
#define WT_FIVE_KANA         3
#define WT_FIVE_REPEAT       4
#define WT_FIVE_CHO_ON       5

//
//  Values for CJK Unified Ideographs Extension A range.
//    0x3400 thru 0x4dbf
//
#define SM_EXT_A                  254       // SM for Extension A
#define AW_EXT_A                  255       // AW for Extension A

//
//  Values for UW extra weights (e.g. Jamo (old Hangul)).
//
#define SM_UW_XW                  255       // SM for extra UW weights


//
//  Script Member Values.
//
#define UNSORTABLE           0
#define NONSPACE_MARK        1
#define EXPANSION            2
#define FAREAST_SPECIAL      3
#define JAMO_SPECIAL         4
#define EXTENSION_A          5

#define PUNCTUATION          6

#define SYMBOL_1             7
#define SYMBOL_2             8
#define SYMBOL_3             9
#define SYMBOL_4             10
#define SYMBOL_5             11

#define NUMERIC_1            12
#define NUMERIC_2            13

#define LATIN                14
#define GREEK                15
#define CYRILLIC             16
#define ARMENIAN             17
#define HEBREW               18
#define ARABIC               19
#define DEVANAGARI           20
#define BENGALI              21
#define GURMUKKHI            22
#define GUJARATI             23
#define ORIYA                24
#define TAMIL                25
#define TELUGU               26
#define KANNADA              27
#define MALAYLAM             28
#define SINHALESE            29
#define THAI                 30
#define LAO                  31
#define TIBETAN              32
#define GEORGIAN             33
#define KANA                 34
#define BOPOMOFO             35
#define HANGUL               36
#define IDEOGRAPH            128

#define MAX_SPECIAL_CASE     SYMBOL_5
#define FIRST_SCRIPT         LATIN


//
//  Calendar Type Values.
//
#define CAL_NO_OPTIONAL      0                          // no optional calendars
#define CAL_LAST             CAL_GREGORIAN_XLIT_FRENCH  // greatest calendar value

//
//  The following calendars are defined in winnls.w:
//
//  #define CAL_GREGORIAN                  1
//  #define CAL_GREGORIAN_US               2
//  #define CAL_JAPAN                      3
//  #define CAL_TAIWAN                     4
//  #define CAL_KOREA                      5
//  #define CAL_HIJRI                      6
//  #define CAL_THAI                       7
//  #define CAL_HEBREW                     8
//  #define CAL_GREGORIAN_ME_FRENCH        9
//  #define CAL_GREGORIAN_ARABIC           10
//  #define CAL_GREGORIAN_XLIT_ENGLISH     11
//  #define CAL_GREGORIAN_XLIT_FRENCH      12
//


//
//  Constants to define range of Unicode private use area.
//
#define PRIVATE_USE_BEGIN    0xe000
#define PRIVATE_USE_END      0xf8ff


//
//  Internal flag for SpecialMBToWC routine.
//
#define MB_INVALID_CHAR_CHECK     MB_ERR_INVALID_CHARS

//
//  Geo values.
//
#define MAX_GEO_STRING_SIZE       1024

//
//  Resource String Table Values.
//
#define RC_STRING_SEPARATOR       '$'

#define RC_LANGUAGE_NAME          0
#define RC_COUNTRY_NAME           1
#define RC_LANGUAGE_GROUP_NAME    2
#define RC_CODE_PAGE_NAME         3
#define RC_GEO_FRIENDLY_NAME      4
#define RC_GEO_OFFICIAL_NAME      5
#define RC_SORT_NAMES             6




////////////////////////////////////////////////////////////////////////////
//
//  Typedef Declarations.
//
////////////////////////////////////////////////////////////////////////////

//
//  Constant Types
//
typedef  LPWORD        P844_TABLE;     // ptr to 8:4:4 table

typedef  LPWORD        PMB_TABLE;      // ptr to MB translation table
typedef  PMB_TABLE     PGLYPH_TABLE;   // ptr to GLYPH translation table
typedef  LPWORD        PDBCS_RANGE;    // ptr to DBCS range
typedef  LPWORD        PDBCS_OFFSETS;  // ptr to DBCS offset section
typedef  LPWORD        PDBCS_TABLE;    // ptr to DBCS translation table
typedef  PVOID         PWC_TABLE;      // ptr to WC translation table
typedef  P844_TABLE    PCTYPE;         // ptr to Character Type table
typedef  P844_TABLE    PCASE;          // ptr to Lower or Upper Case table
typedef  P844_TABLE    PADIGIT;        // ptr to Ascii Digits table
typedef  P844_TABLE    PCZONE;         // ptr to Fold Compat. Zone table
typedef  P844_TABLE    PKANA;          // ptr to Hiragana/Katakana table
typedef  P844_TABLE    PHALFWIDTH;     // ptr to Half Width table
typedef  P844_TABLE    PFULLWIDTH;     // ptr to Full Width table
typedef  P844_TABLE    PCHINESE;       // ptr to Traditional/Simplified Chinese table
typedef  P844_TABLE    PPRECOMP;       // ptr to PreComposed table
typedef  LPWORD        PCOMP_GRID;     // ptr to Composite table 2D grid
typedef  LPWORD        PLOC_INFO;      // ptr to locale information
typedef  LPWORD        PCAL_INFO;      // ptr to calendar information

typedef  DWORD         REVERSE_DW;     // reverse diacritic table
typedef  REVERSE_DW   *PREVERSE_DW;    // ptr to reverse diacritic table
typedef  DWORD         DBL_COMPRESS;   // double compression table
typedef  DBL_COMPRESS *PDBL_COMPRESS;  // ptr to double compression table
typedef  LPWORD        PCOMPRESS;      // ptr to compression table (2 or 3)


//
//  Proc Definition for Code Page DLL Routine.
//
typedef DWORD (*LPFN_CP_PROC)(DWORD, DWORD, LPSTR, int, LPWSTR, int, LPCPINFO);


//
//  CP Information Table Structure (as it is in the data file).
//
typedef struct cp_table_s {
    WORD      CodePage;                // code page number
    WORD      MaxCharSize;             // max length (bytes) of a char
    WORD      wDefaultChar;            // default character (MB)
    WORD      wUniDefaultChar;         // default character (Unicode)
    WORD      wTransDefaultChar;       // translation of wDefaultChar (Unicode)
    WORD      wTransUniDefaultChar;    // translation of wUniDefaultChar (MB)
    BYTE      LeadByte[MAX_LEADBYTES]; // lead byte ranges
} CP_TABLE, *PCP_TABLE;


//
//  Composite Information Structure.
//
typedef struct comp_info_s {
    BYTE           NumBase;            // number base chars in grid
    BYTE           NumNonSp;           // number non space chars in grid
    P844_TABLE     pBase;              // ptr to base char table
    P844_TABLE     pNonSp;             // ptr to nonspace char table
    PCOMP_GRID     pGrid;              // ptr to 2D grid
} COMP_INFO, *PCOMP_INFO;


//
//  Code Page Hash Table Structure.
//
typedef struct cp_hash_s {
    UINT           CodePage;           // codepage ID
    LPFN_CP_PROC   pfnCPProc;          // ptr to code page function proc
    PCP_TABLE      pCPInfo;            // ptr to CPINFO table
    PMB_TABLE      pMBTbl;             // ptr to MB translation table
    PGLYPH_TABLE   pGlyphTbl;          // ptr to GLYPH translation table
    PDBCS_RANGE    pDBCSRanges;        // ptr to DBCS ranges
    PDBCS_OFFSETS  pDBCSOffsets;       // ptr to DBCS offsets
    PWC_TABLE      pWC;                // ptr to WC table
    struct cp_hash_s *pNext;           // ptr to next CP hash node
} CP_HASH, *PCP_HASH;


//
//  Language Exception Header Structure.
//
typedef struct l_except_hdr_s {
    DWORD     Locale;                  // locale id
    DWORD     Offset;                  // offset to exception nodes (words)
    DWORD     NumUpEntries;            // number of upper case entries
    DWORD     NumLoEntries;            // number of lower case entries
} L_EXCEPT_HDR, *PL_EXCEPT_HDR;


//
//  Language Exception Structure.
//
typedef struct l_except_s
{
    WORD      UCP;                     // unicode code point
    WORD      AddAmount;               // amount to add to code point
} L_EXCEPT, *PL_EXCEPT;


//
//  CType header structure.
//
typedef struct ctype_hdr_s {
    WORD      TblSize;                 // size of ctype table
    WORD      MapSize;                 // size of mapping table
} CTYPE_HDR, *PCTYPE_HDR;


//
//  CType Table Structure (Mapping table structure).
//
typedef struct ct_values_s {
    WORD      CType1;                  // ctype 1 value
    WORD      CType2;                  // ctype 2 value
    WORD      CType3;                  // ctype 3 value
} CT_VALUES, *PCT_VALUES;


//
//  Sortkey Structure.
//
typedef struct sortkey_s {

    union {
        struct sm_aw_s {
            BYTE   Alpha;              // alphanumeric weight
            BYTE   Script;             // script member
        } SM_AW;

        WORD  Unicode;                 // unicode weight

    } UW;

    BYTE      Diacritic;               // diacritic weight
    BYTE      Case;                    // case weight (with COMP)

} SORTKEY, *PSORTKEY;


//
//  Extra Weight Structure.
//
typedef struct extra_wt_s {
    BYTE      Four;                    // weight 4
    BYTE      Five;                    // weight 5
    BYTE      Six;                     // weight 6
    BYTE      Seven;                   // weight 7
} EXTRA_WT, *PEXTRA_WT;


//
//  Compression Header Structure.
//  This is the header for the compression tables.
//
typedef struct compress_hdr_s {
    DWORD     Locale;                  // locale id
    DWORD     Offset;                  // offset (in words)
    WORD      Num2;                    // Number of 2 compressions
    WORD      Num3;                    // Number of 3 compressions
} COMPRESS_HDR, *PCOMPRESS_HDR;


//
//  Compression 2 Structure.
//  This is for a 2 code point compression - 2 code points
//  compress to ONE weight.
//
typedef struct compress_2_s {
    WCHAR     UCP1;                    // Unicode code point 1
    WCHAR     UCP2;                    // Unicode code point 2
    SORTKEY   Weights;                 // sortkey weights
} COMPRESS_2, *PCOMPRESS_2;


//
//  Compression 3 Structure.
//  This is for a 3 code point compression - 3 code points
//  compress to ONE weight.
//
typedef struct compress_3_s {
    WCHAR     UCP1;                    // Unicode code point 1
    WCHAR     UCP2;                    // Unicode code point 2
    WCHAR     UCP3;                    // Unicode code point 3
    WCHAR     Reserved;                // dword alignment
    SORTKEY   Weights;                 // sortkey weights
} COMPRESS_3, *PCOMPRESS_3;


//
//  Multiple Weight Structure.
//
typedef struct multiwt_s {
    BYTE      FirstSM;                 // value of first script member
    BYTE      NumSM;                   // number of script members in range
} MULTI_WT, *PMULTI_WT;


//
//  Ideograph Lcid Exception Structure.
//
typedef struct ideograph_lcid_s {
    DWORD     Locale;                  // locale id
    WORD      pFileName[14];           // ptr to file name
} IDEOGRAPH_LCID, *PIDEOGRAPH_LCID;


//
//  Expansion Structure.
//
typedef struct expand_s {
    WCHAR     UCP1;                    // Unicode code point 1
    WCHAR     UCP2;                    // Unicode code point 2
} EXPAND, *PEXPAND;


//
//  Exception Header Structure.
//  This is the header for the exception tables.
//
typedef struct except_hdr_s {
    DWORD     Locale;                  // locale id
    DWORD     Offset;                  // offset to exception nodes (words)
    DWORD     NumEntries;              // number of entries for locale id
} EXCEPT_HDR, *PEXCEPT_HDR;


//
//  Exception Structure.
//
//  NOTE: May also be used for Ideograph Exceptions (4 column tables).
//
typedef struct except_s
{
    WORD      UCP;                     // unicode code point
    WORD      Unicode;                 // unicode weight
    BYTE      Diacritic;               // diacritic weight
    BYTE      Case;                    // case weight
} EXCEPT, *PEXCEPT;


//
//  Ideograph Exception Header Structure.
//
typedef struct ideograph_except_hdr_s
{
    DWORD     NumEntries;              // number of entries in table
    DWORD     NumColumns;              // number of columns in table (2 or 4)
} IDEOGRAPH_EXCEPT_HDR, *PIDEOGRAPH_EXCEPT_HDR;


//
//  Ideograph Exception Structure.
//
typedef struct ideograph_except_s
{
    WORD      UCP;                     // unicode code point
    WORD      Unicode;                 // unicode weight
} IDEOGRAPH_EXCEPT, *PIDEOGRAPH_EXCEPT;


//
//  Locale Information Structures.
//
//  This is the format in which the locale information is kept in the
//  locale data file.  These structures are only used for offsets into
//  the data file, not to store information.
//

//
//  Header at the top of the locale.nls file.
//
typedef struct loc_cal_hdr_s
{
    DWORD     NumLocales;              // number of locales
    DWORD     NumCalendars;            // number of calendars
    DWORD     CalOffset;               // offset to calendar info (words)
} LOC_CAL_HDR, *PLOC_CAL_HDR;

#define LOCALE_HDR_OFFSET    (sizeof(LOC_CAL_HDR) / sizeof(WORD))

//
//  Per entry locale header.
//
//  The locale header structure contains the information given in one entry
//  of the header for the locale information.
//
typedef struct locale_hdr_s {
    DWORD     Locale;                  // locale ID
    DWORD     Offset;                  // offset to locale information
} LOCALE_HDR, *PLOCALE_HDR;

#define LOCALE_HDR_SIZE  (sizeof(LOCALE_HDR) / sizeof(WORD))

//
//  The fixed structure contains the locale information that is of
//  fixed length and in the order in which it is given in the file.
//
typedef struct locale_fixed_s
{
    WORD      DefaultACP;              // default ACP - integer format
    WORD      szILanguage[5];          // language id
    WORD      szICountry[6];           // country id
    WORD      szIGeoID[8];            // geographical location identifier
    WORD      szIDefaultLang[5];       // default language ID
    WORD      szIDefaultCtry[6];       // default country ID
    WORD      szIDefaultACP[6];        // default ansi code page ID
    WORD      szIDefaultOCP[6];        // default oem code page ID
    WORD      szIDefaultMACCP[6];      // default mac code page ID
    WORD      szIDefaultEBCDICCP[6];   // default ebcdic code page ID
    WORD      szIMeasure[2];           // system of measurement
    WORD      szIPaperSize[2];         // default paper size
    WORD      szIDigits[3];            // number of fractional digits
    WORD      szILZero[2];             // leading zeros for decimal
    WORD      szINegNumber[2];         // negative number format
    WORD      szIDigitSubstitution[2]; // digit substitution
    WORD      szICurrDigits[3];        // # local monetary fractional digits
    WORD      szIIntlCurrDigits[3];    // # intl monetary fractional digits
    WORD      szICurrency[2];          // positive currency format
    WORD      szINegCurr[3];           // negative currency format
    WORD      szIPosSignPosn[2];       // format of positive sign
    WORD      szIPosSymPrecedes[2];    // if mon symbol precedes positive
    WORD      szIPosSepBySpace[2];     // if mon symbol separated by space
    WORD      szINegSignPosn[2];       // format of negative sign
    WORD      szINegSymPrecedes[2];    // if mon symbol precedes negative
    WORD      szINegSepBySpace[2];     // if mon symbol separated by space
    WORD      szITime[2];              // time format
    WORD      szITLZero[2];            // leading zeros for time field
    WORD      szITimeMarkPosn[2];      // time marker position
    WORD      szIDate[2];              // short date order
    WORD      szICentury[2];           // century format (short date)
    WORD      szIDayLZero[2];          // leading zeros for day field (short date)
    WORD      szIMonLZero[2];          // leading zeros for month field (short date)
    WORD      szILDate[2];             // long date order
    WORD      szICalendarType[3];      // type of calendar
    WORD      szIFirstDayOfWk[2];      // first day of week
    WORD      szIFirstWkOfYr[2];       // first week of year
    WORD      szFontSignature[MAX_FONTSIGNATURE];
} LOCALE_FIXED, *PLOCALE_FIXED;

//
//  The variable structure contains the offsets to the various pieces of
//  locale information that is of variable length.  It is in the order
//  in which it is given in the file.
//
typedef struct locale_var_s
{
    WORD      SEngLanguage;            // English language name
    WORD      SAbbrevLang;             // abbreviated language name
    WORD      SAbbrevLangISO;          // ISO abbreviated language name
    WORD      SNativeLang;             // native language name
    WORD      SEngCountry;             // English country name
    WORD      SAbbrevCtry;             // abbreviated country name
    WORD      SAbbrevCtryISO;          // ISO abbreviated country name
    WORD      SNativeCtry;             // native country name
    WORD      SList;                   // list separator
    WORD      SDecimal;                // decimal separator
    WORD      SThousand;               // thousands separator
    WORD      SGrouping;               // grouping of digits
    WORD      SNativeDigits;           // native digits 0-9
    WORD      SCurrency;               // local monetary symbol
    WORD      SIntlSymbol;             // international monetary symbol
    WORD      SEngCurrName;            // English currency name
    WORD      SNativeCurrName;         // native currency name
    WORD      SMonDecSep;              // monetary decimal separator
    WORD      SMonThousSep;            // monetary thousands separator
    WORD      SMonGrouping;            // monetary grouping of digits
    WORD      SPositiveSign;           // positive sign
    WORD      SNegativeSign;           // negative sign
    WORD      STimeFormat;             // time format
    WORD      STime;                   // time separator
    WORD      S1159;                   // AM designator
    WORD      S2359;                   // PM designator
    WORD      SShortDate;              // short date format
    WORD      SDate;                   // date separator
    WORD      SYearMonth;              // year month format
    WORD      SLongDate;               // long date format
    WORD      IOptionalCal;            // additional calendar type(s)
    WORD      SDayName1;               // day name 1
    WORD      SDayName2;               // day name 2
    WORD      SDayName3;               // day name 3
    WORD      SDayName4;               // day name 4
    WORD      SDayName5;               // day name 5
    WORD      SDayName6;               // day name 6
    WORD      SDayName7;               // day name 7
    WORD      SAbbrevDayName1;         // abbreviated day name 1
    WORD      SAbbrevDayName2;         // abbreviated day name 2
    WORD      SAbbrevDayName3;         // abbreviated day name 3
    WORD      SAbbrevDayName4;         // abbreviated day name 4
    WORD      SAbbrevDayName5;         // abbreviated day name 5
    WORD      SAbbrevDayName6;         // abbreviated day name 6
    WORD      SAbbrevDayName7;         // abbreviated day name 7
    WORD      SMonthName1;             // month name 1
    WORD      SMonthName2;             // month name 2
    WORD      SMonthName3;             // month name 3
    WORD      SMonthName4;             // month name 4
    WORD      SMonthName5;             // month name 5
    WORD      SMonthName6;             // month name 6
    WORD      SMonthName7;             // month name 7
    WORD      SMonthName8;             // month name 8
    WORD      SMonthName9;             // month name 9
    WORD      SMonthName10;            // month name 10
    WORD      SMonthName11;            // month name 11
    WORD      SMonthName12;            // month name 12
    WORD      SMonthName13;            // month name 13 (if exists)
    WORD      SAbbrevMonthName1;       // abbreviated month name 1
    WORD      SAbbrevMonthName2;       // abbreviated month name 2
    WORD      SAbbrevMonthName3;       // abbreviated month name 3
    WORD      SAbbrevMonthName4;       // abbreviated month name 4
    WORD      SAbbrevMonthName5;       // abbreviated month name 5
    WORD      SAbbrevMonthName6;       // abbreviated month name 6
    WORD      SAbbrevMonthName7;       // abbreviated month name 7
    WORD      SAbbrevMonthName8;       // abbreviated month name 8
    WORD      SAbbrevMonthName9;       // abbreviated month name 9
    WORD      SAbbrevMonthName10;      // abbreviated month name 10
    WORD      SAbbrevMonthName11;      // abbreviated month name 11
    WORD      SAbbrevMonthName12;      // abbreviated month name 12
    WORD      SAbbrevMonthName13;      // abbreviated month name 13 (if exists)
    WORD      SEndOfLocale;            // end of locale information
} LOCALE_VAR, *PLOCALE_VAR;


//
//  Per entry calendar header.
//
//  The calendar header structure contains the information given in one entry
//  of the header for the calendar information.
//
typedef struct calendar_hdr_s
{
    WORD      Calendar;                // calendar id
    WORD      Offset;                  // offset to calendar info (words)
} CALENDAR_HDR, *PCALENDAR_HDR;

#define CALENDAR_HDR_SIZE  (sizeof(CALENDAR_HDR) / sizeof(WORD))

//
//  The variable structure contains the offsets to the various pieces of
//  calendar information that is of variable length.  It is in the order
//  in which it is given in the file.
//
//  The NumRanges value is the number of era ranges.  If this value is zero,
//  then there are no year offsets.
//
//  The IfNames value is a boolean.  If it is 0, then there are NO special
//  day or month names for the calendar.  If it is 1, then there ARE
//  special day and month names for the calendar.
//
//  The rest of the values are offsets to the appropriate strings.
//
typedef struct calendar_var_s
{
    WORD      NumRanges;               // number of era ranges
    WORD      IfNames;                 // if any day or month names exist
    WORD      SCalendar;               // calendar id
    WORD      STwoDigitYearMax;        // two digit year max
    WORD      SEraRanges;              // era ranges
    WORD      SShortDate;              // short date format
    WORD      SYearMonth;              // year month format
    WORD      SLongDate;               // long date format
    WORD      SDayName1;               // day name 1
    WORD      SDayName2;               // day name 2
    WORD      SDayName3;               // day name 3
    WORD      SDayName4;               // day name 4
    WORD      SDayName5;               // day name 5
    WORD      SDayName6;               // day name 6
    WORD      SDayName7;               // day name 7
    WORD      SAbbrevDayName1;         // abbreviated day name 1
    WORD      SAbbrevDayName2;         // abbreviated day name 2
    WORD      SAbbrevDayName3;         // abbreviated day name 3
    WORD      SAbbrevDayName4;         // abbreviated day name 4
    WORD      SAbbrevDayName5;         // abbreviated day name 5
    WORD      SAbbrevDayName6;         // abbreviated day name 6
    WORD      SAbbrevDayName7;         // abbreviated day name 7
    WORD      SMonthName1;             // month name 1
    WORD      SMonthName2;             // month name 2
    WORD      SMonthName3;             // month name 3
    WORD      SMonthName4;             // month name 4
    WORD      SMonthName5;             // month name 5
    WORD      SMonthName6;             // month name 6
    WORD      SMonthName7;             // month name 7
    WORD      SMonthName8;             // month name 8
    WORD      SMonthName9;             // month name 9
    WORD      SMonthName10;            // month name 10
    WORD      SMonthName11;            // month name 11
    WORD      SMonthName12;            // month name 12
    WORD      SMonthName13;            // month name 13
    WORD      SAbbrevMonthName1;       // abbreviated month name 1
    WORD      SAbbrevMonthName2;       // abbreviated month name 2
    WORD      SAbbrevMonthName3;       // abbreviated month name 3
    WORD      SAbbrevMonthName4;       // abbreviated month name 4
    WORD      SAbbrevMonthName5;       // abbreviated month name 5
    WORD      SAbbrevMonthName6;       // abbreviated month name 6
    WORD      SAbbrevMonthName7;       // abbreviated month name 7
    WORD      SAbbrevMonthName8;       // abbreviated month name 8
    WORD      SAbbrevMonthName9;       // abbreviated month name 9
    WORD      SAbbrevMonthName10;      // abbreviated month name 10
    WORD      SAbbrevMonthName11;      // abbreviated month name 11
    WORD      SAbbrevMonthName12;      // abbreviated month name 12
    WORD      SAbbrevMonthName13;      // abbreviated month name 13
    WORD      SEndOfCalendar;          // end of calendar information
} CALENDAR_VAR, *PCALENDAR_VAR;

//
//  IOptionalCalendar structure (locale info).
//
typedef struct opt_cal_s
{
    WORD      CalId;                   // calendar id
    WORD      Offset;                  // offset to next optional calendar
    WORD      pCalStr[1];              // calendar id string (variable length)
//  WORD      pCalNameStr[1];          // calendar name string (variable length)
} OPT_CAL, *POPT_CAL;


//
//  SEraRanges structure inside calendar info.
//
typedef struct era_range_s
{
    WORD      Month;                   // month of era beginning
    WORD      Day;                     // day of era beginning
    WORD      Year;                    // year of era beginning
    WORD      Offset;                  // offset to next era info block
    WORD      pYearStr[1];             // year string (variable length)
//  WORD      pEraNameStr[1];          // era name string (variable length)
} ERA_RANGE, *PERA_RANGE;


//
//  Locale Hash Table Structure.
//
typedef struct loc_hash_s {
    LCID           Locale;             // locale ID
    PLOCALE_VAR    pLocaleHdr;         // ptr to locale header info
    PLOCALE_FIXED  pLocaleFixed;       // ptr to locale fixed size info
    PCASE          pUpperCase;         // ptr to Upper Case table
    PCASE          pLowerCase;         // ptr to Lower Case table
    PCASE          pUpperLinguist;     // ptr to Upper Case Linguistic table
    PCASE          pLowerLinguist;     // ptr to Lower Case Linguistic table
    PSORTKEY       pSortkey;           // ptr to sortkey table
    BOOL           IfReverseDW;        // if DW should go from right to left
    BOOL           IfCompression;      // if compression code points exist
    BOOL           IfDblCompression;   // if double compression exists
    BOOL           IfIdeographFailure; // if ideograph table failed to load
    PCOMPRESS_HDR  pCompHdr;           // ptr to compression header
    PCOMPRESS_2    pCompress2;         // ptr to 2 compression table
    PCOMPRESS_3    pCompress3;         // ptr to 3 compression table
    struct loc_hash_s *pNext;          // ptr to next locale hash node
} LOC_HASH, *PLOC_HASH;


//
//  Hash Table Pointers.
//
typedef  PCP_HASH  *PCP_HASH_TBL;      // ptr to a code page hash table
typedef  PLOC_HASH *PLOC_HASH_TBL;     // ptr to a locale hash table


//
//  Geo Information structure. This structure holds information about
//  a geographical location on earth.
//
typedef struct tagGeoInfo {
    GEOID       GeoId;
    WCHAR       szLatitude[12];
    WCHAR       szLongitude[12];
    GEOCLASS    GeoClass;
    GEOID       ParentGeoId;
    WCHAR       szISO3166Abbrev2[4];
    WCHAR       szISO3166Abbrev3[4];
    WORD        wISO3166;
    WORD        Reserved;              // dword alignment
} GEOINFO, *PGEOINFO;


//
//  GEOID/LCID structure. This structure is used to navigate through
//  the table that maps corresponding Language ID and Geo ID.
//
typedef struct tagGEOIDLCID {
    LCID    lcid;
    GEOID   GeoId;
    LANGID  LangId;
    WORD    Reserved;                  // dword alignment
} GEOLCID, *PGEOLCID;


//
//  GEO tables structure. This structure is used to get information
//  related to all geo tables.
//
typedef struct tagGeoTableHdr {
    WCHAR           szSig[4];
    unsigned long   nFileSize;
    DWORD           dwOffsetGeoInfo;
    long            nGeoInfo;
    DWORD           dwOffsetGeoLCID;
    long            nGeoLCID;
} GEOTABLEHDR, *PGEOTABLEHDR;


//
//  Jamo Sequence Sorting Info.
//
typedef struct {
    BYTE m_bOld;                  // sequence occurs only in old Hangul flag
    CHAR m_chLeadingIndex;        // indices used to locate prior modern Hangul syllable
    CHAR m_chVowelIndex;
    CHAR m_chTrailingIndex;
    BYTE m_ExtraWeight;           // extra weights that distinguish this from
                                  //   other old Hangul syllables, depending
                                  //   on the jamo, this can be a weight for
                                  //   leading jamo, vowel jamo, or trailing jamo.
} JAMO_SORT_INFO, *PJAMO_SORT_INFO;

//
//  Jamo Index Table Entry.
//
typedef struct {
    JAMO_SORT_INFO SortInfo;      // sequence sorting info
    BYTE Index;                   // index into the composition array
    BYTE TransitionCount;         // # of possible transitions from this state
    BYTE Reserved;                // word alignment
} JAMO_TABLE, *PJAMO_TABLE;


//
//  Jamo Combination Table Entry.
//
//  NOTE: Make sure this structure is WORD aligned.  Otherwise, code will
//        fail in GetDefaultSortTable().
//
typedef struct {
    WCHAR m_wcCodePoint;          // Code point value that enters this state
    JAMO_SORT_INFO m_SortInfo;    // Sequence sorting info
    BYTE m_bTransitionCount;      // # of possible transitions from this state
} JAMO_COMPOSE_STATE, *PJAMO_COMPOSE_STATE;


//
//  Table Pointers Structure.  This structure contains pointers to
//  the various tables needed for the NLS APIs.  There should be only
//  ONE of these for each process, and the information should be
//  global to the process.
//
#define NUM_SM     256                  // total number of script members
#define NUM_CAL    64                   // total number calendars allowed

typedef struct tbl_ptrs_s {
    PCP_HASH_TBL    pCPHashTbl;         // ptr to Code Page hash table
    PLOC_HASH_TBL   pLocHashTbl;        // ptr to Locale hash table
    PLOC_INFO       pLocaleInfo;        // ptr to locale table (all locales)
    DWORD           NumCalendars;       // number of calendars
    PCAL_INFO       pCalendarInfo;      // ptr to beginning of calendar info
    PCAL_INFO       pCalTbl[NUM_CAL];   // ptr to calendar table array
    P844_TABLE      pDefaultLanguage;   // ptr to default language table
    P844_TABLE      pLinguistLanguage;  // ptr to default linguistic lang table
    LARGE_INTEGER   LinguistLangSize;   // size of linguistic lang table
    int             NumLangException;   // number of language exceptions
    PL_EXCEPT_HDR   pLangExceptHdr;     // ptr to lang exception table header
    PL_EXCEPT       pLangException;     // ptr to lang exception tables
    PCT_VALUES      pCTypeMap;          // ptr to Ctype Mapping table
    PCTYPE          pCType844;          // ptr to Ctype 8:4:4 table
    PADIGIT         pADigit;            // ptr to Ascii Digits table
    PCZONE          pCZone;             // ptr to Compatibility Zone table
    PKANA           pHiragana;          // ptr to Hiragana table
    PKANA           pKatakana;          // ptr to Katakana table
    PHALFWIDTH      pHalfWidth;         // ptr to Half Width table
    PFULLWIDTH      pFullWidth;         // ptr to Full Width table
    PCHINESE        pTraditional;       // ptr to Traditional Chinese table
    PCHINESE        pSimplified;        // ptr to Simplified Chinese table
    PPRECOMP        pPreComposed;       // ptr to PreComposed Table
    PCOMP_INFO      pComposite;         // ptr to Composite info structure
    DWORD           NumReverseDW;       // number of REVERSE DIACRITICS
    DWORD           NumDblCompression;  // number of DOUBLE COMPRESSION locales
    DWORD           NumIdeographLcid;   // number of IDEOGRAPH LCIDs
    DWORD           NumExpansion;       // number of EXPANSIONS
    DWORD           NumCompression;     // number of COMPRESSION locales
    DWORD           NumException;       // number of EXCEPTION locales
    DWORD           NumMultiWeight;     // number of MULTIPLE WEIGHTS
    int             NumJamoIndex;       // number of entires for Jamo Index Table
    int             NumJamoComposition; // number of entires for Jamo Composition Table
    PSORTKEY        pDefaultSortkey;    // ptr to default sortkey table
    LARGE_INTEGER   DefaultSortkeySize; // size of default sortkey section
    PREVERSE_DW     pReverseDW;         // ptr to reverse diacritic table
    PDBL_COMPRESS   pDblCompression;    // ptr to double compression table
    PIDEOGRAPH_LCID pIdeographLcid;     // ptr to ideograph lcid table
    PEXPAND         pExpansion;         // ptr to expansion table
    PCOMPRESS_HDR   pCompressHdr;       // ptr to compression table header
    PCOMPRESS       pCompression;       // ptr to compression tables
    PEXCEPT_HDR     pExceptHdr;         // ptr to exception table header
    PEXCEPT         pException;         // ptr to exception tables
    PMULTI_WT       pMultiWeight;       // ptr to multiple weights table
    BYTE            SMWeight[NUM_SM];   // script member weights
    PJAMO_TABLE     pJamoIndex;         // ptr ot Jamo Index table
    PJAMO_COMPOSE_STATE pJamoComposition;  // ptr to Jamo Composition state machine table
    long            nGeoInfo;           // number of GEOINFO entries
    PGEOINFO        pGeoInfo;           // ptr to gegraphical info location table
    long            nGeoLCID;           // number of GEOID/LCID entries
    PGEOLCID        pGeoLCID;           // ptr to GEOID/LCID mapping table
} TBL_PTRS, *PTBL_PTRS;

typedef struct nls_locale_cache
{
    NLS_USER_INFO NlsInfo;              // NLS cached information
    HKEY CurrentUserKeyHandle;          // Cached key handle thread impersonation

} NLS_LOCAL_CACHE, *PNLS_LOCAL_CACHE;


//
//  Generic Enum Proc Definitions.
//
typedef BOOL (CALLBACK* NLS_ENUMPROC)(PVOID);
typedef BOOL (CALLBACK* NLS_ENUMPROCEX)(PVOID, DWORD);
typedef BOOL (CALLBACK* NLS_ENUMPROC2)(DWORD, DWORD, PVOID, LONG_PTR);
typedef BOOL (CALLBACK* NLS_ENUMPROC3)(DWORD, PVOID, PVOID, DWORD, LONG_PTR);
typedef BOOL (CALLBACK* NLS_ENUMPROC4)(PVOID, LONG_PTR);





////////////////////////////////////////////////////////////////////////////
//
//  Macro Definitions.
//
////////////////////////////////////////////////////////////////////////////

//
//  Get the wide character count from a byte count.
//
#define GET_WC_COUNT(bc)          ((bc) / sizeof(WCHAR))

//
//  Get the data pointer for the KEY_VALUE_FULL_INFORMATION structure.
//
#define GET_VALUE_DATA_PTR(p)     ((LPWSTR)((PBYTE)(p) + (p)->DataOffset))

//
//  Macros For High and Low Nibbles of a BYTE.
//
#define LO_NIBBLE(b)              ((BYTE)((BYTE)(b) & 0xF))
#define HI_NIBBLE(b)              ((BYTE)(((BYTE)(b) >> 4) & 0xF))

//
//  Macros for Extracting the 8:4:4 Index Values.
//
#define GET8(w)                   (HIBYTE(w))
#define GETHI4(w)                 (HI_NIBBLE(LOBYTE(w)))
#define GETLO4(w)                 (LO_NIBBLE(LOBYTE(w)))


//
//  Macros for setting and checking most significant bit of flag.
//
#define SET_MSB(fl)               (fl |= MSB_FLAG)
#define IS_MSB(fl)                (fl & MSB_FLAG)

//
//  Macro to check if more than one bit is set.
//  Returns 1 if more than one bit set, 0 otherwise.
//
#define MORE_THAN_ONE(f, bits)    (((f & bits) - 1) & (f & bits))

//
//  Macros for single and double byte code pages.
//
#define IS_SBCS_CP(pHash)         (pHash->pCPInfo->MaxCharSize == 1)
#define IS_DBCS_CP(pHash)         (pHash->pCPInfo->MaxCharSize == 2)


////////////////////////////////////////////////////////////////////////////
//
//  TRAVERSE_844_B
//
//  Traverses the 8:4:4 translation table for the given wide character.  It
//  returns the final value of the 8:4:4 table, which is a BYTE in length.
//
//  NOTE: Offsets in table are in BYTES.
//
//    Broken Down Version:
//    --------------------
//        Incr = pTable[GET8(wch)] / sizeof(WORD);
//        Incr = pTable[Incr + GETHI4(wch)];
//        Value = (BYTE *)pTable[Incr + GETLO4(wch)];
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define TRAVERSE_844_B(pTable, wch)                                        \
    (((BYTE *)pTable)[pTable[(pTable[GET8(wch)] / sizeof(WORD)) +          \
                               GETHI4(wch)] +                              \
                      GETLO4(wch)])


////////////////////////////////////////////////////////////////////////////
//
//  TRAVERSE_844_W
//
//  Traverses the 8:4:4 translation table for the given wide character.  It
//  returns the final value of the 8:4:4 table, which is a WORD in length.
//
//    Broken Down Version:
//    --------------------
//        Incr = pTable[GET8(wch)];
//        Incr = pTable[Incr + GETHI4(wch)];
//        Value = pTable[Incr + GETLO4(wch)];
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define TRAVERSE_844_W(pTable, wch)                                        \
    (pTable[pTable[pTable[GET8(wch)] + GETHI4(wch)] + GETLO4(wch)])


////////////////////////////////////////////////////////////////////////////
//
//  TRAVERSE_844_D
//
//  Traverses the 8:4:4 translation table for the given wide character.  It
//  fills in the final word values, Value1 and Value2.  The final value of the
//  8:4:4 table is a DWORD, so both Value1 and Value2 are filled in.
//
//    Broken Down Version:
//    --------------------
//        Incr = pTable[GET8(wch)];
//        Incr = pTable[Incr + GETHI4(wch)];
//        pTable += Incr + (GETLO4(wch) * 2);
//        Value1 = pTable[0];
//        Value2 = pTable[1];
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define TRAVERSE_844_D(pTable, wch, Value1, Value2)                        \
{                                                                          \
    pTable += pTable[pTable[GET8(wch)] + GETHI4(wch)] + (GETLO4(wch) * 2); \
    Value1 = pTable[0];                                                    \
    Value2 = pTable[1];                                                    \
}


////////////////////////////////////////////////////////////////////////////
//
//  GET_INCR_VALUE
//
//  Gets the value of a given wide character from the given 8:4:4 table.  It
//  then uses the value as an increment by adding it to the given wide
//  character code point.
//
//  NOTE:  Whenever there is no translation for the given code point, the
//         tables will return an increment value of 0.  This way, the
//         wide character passed in is the same value that is returned.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_INCR_VALUE(p844Tbl, wch)                                       \
     ((WCHAR)(wch + TRAVERSE_844_W(p844Tbl, wch)))


////////////////////////////////////////////////////////////////////////////
//
//  GET_LOWER_UPPER_CASE
//
//  Gets the lower/upper case value of a given wide character.  If a
//  lower/upper case value exists, it returns the lower/upper case wide
//  character.  Otherwise, it returns the same character passed in wch.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_LOWER_UPPER_CASE(pCaseTbl, wch)                                \
    (GET_INCR_VALUE(pCaseTbl, wch))


////////////////////////////////////////////////////////////////////////////
//
//  GET_ASCII_DIGITS
//
//  Gets the ascii translation for the given digit character.  If no
//  translation is found, then the given character is returned.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_ASCII_DIGITS(pADigit, wch)                                     \
    (GET_INCR_VALUE(pADigit, wch))


////////////////////////////////////////////////////////////////////////////
//
//  GET_FOLD_CZONE
//
//  Gets the translation for the given compatibility zone character.  If no
//  translation is found, then the given character is returned.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_FOLD_CZONE(pCZone, wch)                                        \
    (GET_INCR_VALUE(pCZone, wch))


////////////////////////////////////////////////////////////////////////////
//
//  GET_KANA
//
//  Gets the Hiragana/Katakana equivalent for the given Katakana/Hiragana
//  character.  If no translation is found, then the given character is
//  returned.
//
//  DEFINED AS A MACRO.
//
//  07-14-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_KANA(pKana, wch)                                               \
    (GET_INCR_VALUE(pKana, wch))


////////////////////////////////////////////////////////////////////////////
//
//  GET_HALF_WIDTH
//
//  Gets the Half Width equivalent for the given Full Width character.  If no
//  translation is found, then the given character is returned.
//
//  DEFINED AS A MACRO.
//
//  07-14-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_HALF_WIDTH(pHalf, wch)                                         \
    (GET_INCR_VALUE(pHalf, wch))


////////////////////////////////////////////////////////////////////////////
//
//  GET_FULL_WIDTH
//
//  Gets the Full Width equivalent for the given Half Width character.  If no
//  translation is found, then the given character is returned.
//
//  DEFINED AS A MACRO.
//
//  07-14-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_FULL_WIDTH(pFull, wch)                                         \
    (GET_INCR_VALUE(pFull, wch))


////////////////////////////////////////////////////////////////////////////
//
//  GET_CHINESE
//
//  Gets the Traditional/Simplified Chinese translation for the given
//  Simplified/Traditional Chinese character.  If no translation is found,
//  then the given character is returned.
//
//  DEFINED AS A MACRO.
//
//  05-07-96    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_CHINESE(pChinese, wch)                                         \
    (GET_INCR_VALUE(pChinese, wch))


////////////////////////////////////////////////////////////////////////////
//
//  GET_CTYPE
//
//  Gets the ctype information for a given wide character.  If the ctype
//  information exists, it returns it.  Otherwise, it returns 0.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_CTYPE(wch, offset)                                             \
    ((((PCT_VALUES)(pTblPtrs->pCTypeMap)) +                                \
      (TRAVERSE_844_B((pTblPtrs->pCType844), wch)))->offset)


////////////////////////////////////////////////////////////////////////////
//
//  GET_BASE_CHAR
//
//  Gets the base character of a given precomposed character.  If the
//  composite form is found, it returns the base character.  Otherwise,
//  it returns 0 for failure.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_BASE_CHAR(wch, Base)                                           \
{                                                                          \
    WCHAR NonSp;                  /* nonspacing character */               \
    WCHAR NewBase;                /* base character - temp holder */       \
                                                                           \
                                                                           \
    /*                                                                     \
     *  Get composite characters.                                          \
     */                                                                    \
    if (GetCompositeChars(wch, &NonSp, &Base))                             \
    {                                                                      \
        while (GetCompositeChars(Base, &NonSp, &NewBase))                  \
        {                                                                  \
            Base = NewBase;                                                \
        }                                                                  \
    }                                                                      \
    else                                                                   \
    {                                                                      \
        /*                                                                 \
         *  Return failure - no composite form.                            \
         */                                                                \
        Base = 0;                                                          \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  SORTKEY WEIGHT MACROS
//
//  Parse out the different sortkey weights from a DWORD value.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define GET_SCRIPT_MEMBER(pwt)  ( (BYTE)(((PSORTKEY)(pwt))->UW.SM_AW.Script) )
#define GET_ALPHA_NUMERIC(pwt)  ( (BYTE)(((PSORTKEY)(pwt))->UW.SM_AW.Alpha) )

#define GET_UNICODE(pwt)        ( (WORD)(((PSORTKEY)(pwt))->UW.Unicode) )

#define GET_UNICODE_SM(pwt, sm) ( (WORD)(((PSORTKEY)(pwt))->UW.Unicode) )

#define GET_UNICODE_MOD(pwt, modify_sm)                                    \
    ( (modify_sm) ?                                                        \
        ((WORD)                                                            \
         ((((WORD)((pTblPtrs->SMWeight)[GET_SCRIPT_MEMBER(pwt)])) << 8) |  \
          (WORD)GET_ALPHA_NUMERIC(pwt))) :                                 \
        ((WORD)(((PSORTKEY)(pwt))->UW.Unicode)) )

#define GET_UNICODE_SM_MOD(pwt, sm, modify_sm)                             \
    ( (modify_sm) ?                                                        \
        ((WORD)                                                            \
         ((((WORD)((pTblPtrs->SMWeight)[sm])) << 8) |                      \
          (WORD)GET_ALPHA_NUMERIC(pwt))) :                                 \
        ((WORD)(((PSORTKEY)(pwt))->UW.Unicode)) )

#define MAKE_UNICODE_WT(sm, aw, modify_sm)                                 \
    ( (modify_sm) ?                                                        \
        ((WORD)((((WORD)((pTblPtrs->SMWeight)[sm])) << 8) | (WORD)(aw))) : \
        ((WORD)((((WORD)(sm)) << 8) | (WORD)(aw))) )

#define UNICODE_WT(pwt)           ( (WORD)(((PSORTKEY)(pwt))->UW.Unicode) )

#define GET_DIACRITIC(pwt)        ( (BYTE)(((PSORTKEY)(pwt))->Diacritic) )

#define GET_CASE(pwt)             ( (BYTE)((((PSORTKEY)(pwt))->Case) & CASE_MASK) )

#define CASE_WT(pwt)              ( (BYTE)(((PSORTKEY)(pwt))->Case) )

#define GET_COMPRESSION(pwt)      ( (BYTE)((((PSORTKEY)(pwt))->Case) & COMPRESS_3_MASK) )

#define GET_EXPAND_INDEX(pwt)     ( (BYTE)(((PSORTKEY)(pwt))->UW.SM_AW.Alpha) )

#define GET_SPECIAL_WEIGHT(pwt)   ( (WORD)(((PSORTKEY)(pwt))->UW.Unicode) )

//  position returned is backwards - byte reversal
#define GET_POSITION_SW(pos)      ( (WORD)(((pos) << 2) | SW_POSITION_MASK) )


#define GET_WT_FOUR(pwt)          ( (BYTE)(((PEXTRA_WT)(pwt))->Four) )
#define GET_WT_FIVE(pwt)          ( (BYTE)(((PEXTRA_WT)(pwt))->Five) )
#define GET_WT_SIX(pwt)           ( (BYTE)(((PEXTRA_WT)(pwt))->Six) )
#define GET_WT_SEVEN(pwt)         ( (BYTE)(((PEXTRA_WT)(pwt))->Seven) )


#define MAKE_SORTKEY_DWORD(wt)    ( (DWORD)(*((LPDWORD)(&(wt)))) )

#define MAKE_EXTRA_WT_DWORD(wt)   ( (DWORD)(*((LPDWORD)(&(wt)))) )

#define GET_DWORD_WEIGHT(pHashN, wch)                                      \
    ( MAKE_SORTKEY_DWORD(((pHashN)->pSortkey)[wch]) )

#define GET_EXPANSION_1(pwt)                                               \
    ( ((pTblPtrs->pExpansion)[GET_EXPAND_INDEX(pwt)]).UCP1 )

#define GET_EXPANSION_2(pwt)                                               \
    ( ((pTblPtrs->pExpansion)[GET_EXPAND_INDEX(pwt)]).UCP2 )




#define IS_SYMBOL(pSkey, wch)                                              \
    ( (GET_SCRIPT_MEMBER(&((pSkey)[wch])) >= PUNCTUATION) &&               \
      (GET_SCRIPT_MEMBER(&((pSkey)[wch])) <= SYMBOL_5) )

#define IS_NONSPACE_ONLY(pSkey, wch)                                       \
    ( GET_SCRIPT_MEMBER(&((pSkey)[wch])) == NONSPACE_MARK )

#define IS_NONSPACE(pSkey, wch)                                            \
    ( (GET_SCRIPT_MEMBER(&((pSkey)[wch])) == NONSPACE_MARK) ||             \
      (GET_DIACRITIC(&((pSkey)[wch])) > MIN_DW) )

#define IS_ALPHA(ctype1)          ( (ctype1) & C1_ALPHA )




#define IS_KOREAN(lcid)                                                    \
    ( LANGIDFROMLCID(lcid) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN) )




////////////////////////////////////////////////////////////////////////////
//
//  CHECK_SPECIAL_LOCALES
//
//  Checks for the special locale values and sets the Locale to the
//  appropriate value.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define CHECK_SPECIAL_LOCALES(Locale, UseCachedLocaleId)                    \
{                                                                           \
    /*                                                                      \
     *  Check for special locale values.                                    \
     */                                                                     \
    if (Locale == LOCALE_SYSTEM_DEFAULT)                                    \
    {                                                                       \
        /*                                                                  \
         *  Get the System Default locale value.                            \
         */                                                                 \
        Locale = gSystemLocale;                                             \
    }                                                                       \
    else if ((Locale == LOCALE_NEUTRAL) || (Locale == LOCALE_USER_DEFAULT)) \
    {                                                                       \
        /*                                                                  \
         *  Get the User locale value.                                      \
         */                                                                 \
        if (!UseCachedLocaleId)                                             \
        {                                                                   \
            Locale = GetUserDefaultLCID();                                  \
        }                                                                   \
        else                                                                \
        {                                                                   \
            Locale = pNlsUserInfo->UserLocaleId;                            \
        }                                                                   \
    }                                                                       \
    /*                                                                      \
     *  Check for a valid primary language and a neutral sublanguage.       \
     */                                                                     \
    else if (SUBLANGID(LANGIDFROMLCID(Locale)) == SUBLANG_NEUTRAL)          \
    {                                                                       \
        /*                                                                  \
         *  Re-form the locale id using the primary language and the        \
         *  default sublanguage.                                            \
         */                                                                 \
        Locale = MAKELCID(MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(Locale)), \
                                     SUBLANG_DEFAULT),                      \
                          SORTIDFROMLCID(Locale));                          \
    }                                                                       \
}


////////////////////////////////////////////////////////////////////////////
//
//  IS_INVALID_LOCALE
//
//  Checks to see that only the proper bits are used in the locale.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define NLS_VALID_LOCALE_MASK          0x000fffff
#define IS_INVALID_LOCALE(Locale)      ( Locale & ~NLS_VALID_LOCALE_MASK )


////////////////////////////////////////////////////////////////////////////
//
//  VALIDATE_LANGUAGE
//
//  Checks that the given Locale contains a valid language id.  It does so
//  by making sure the appropriate casing and sorting tables are present.
//  If the language is valid, pLocHashN will be non-NULL.  Otherwise,
//  pLocHashN will be NULL.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define VALIDATE_LANGUAGE(Locale, pLocHashN, dwFlags, UseCachedLocaleId)   \
{                                                                          \
    /*                                                                     \
     *  Check the system locale first for speed.  This is the most         \
     *  likely one to be used.                                             \
     */                                                                    \
    if (Locale == gSystemLocale)                                           \
    {                                                                      \
        pLocHashN = gpSysLocHashN;                                         \
    }                                                                      \
    /*                                                                     \
     *  Check the invariant locale second for speed.  This is the second   \
     *  most likely one to be used.                                        \
     */                                                                    \
    else if (Locale == LOCALE_INVARIANT)                                   \
    {                                                                      \
        pLocHashN = gpInvLocHashN;                                         \
    }                                                                      \
    else                                                                   \
    {                                                                      \
        /*                                                                 \
         *  Check special locale values.                                   \
         */                                                                \
        CHECK_SPECIAL_LOCALES(Locale, UseCachedLocaleId);                  \
                                                                           \
        /*                                                                 \
         *  If the locale is the system default, then the hash node is     \
         *  already stored in a global.                                    \
         */                                                                \
        if (Locale == gSystemLocale)                                       \
        {                                                                  \
            pLocHashN = gpSysLocHashN;                                     \
        }                                                                  \
        else if (IS_INVALID_LOCALE(Locale))                                \
        {                                                                  \
            pLocHashN = NULL;                                              \
        }                                                                  \
        else                                                               \
        {                                                                  \
            /*                                                             \
             *  Need to make sure the locale value is valid.  Need to      \
             *  check the locale file to see if the locale is supported.   \
             */                                                            \
            pLocHashN = GetLocHashNode(Locale);                            \
                                                                           \
            if (pLocHashN != NULL)                                         \
            {                                                              \
                /*                                                         \
                 *  Make sure the appropriate casing and sorting tables    \
                 *  are in the system.                                     \
                 *                                                         \
                 *  NOTE:  If the call fails, pLocHashN will be NULL.      \
                 */                                                        \
                pLocHashN = GetLangHashNode(Locale, dwFlags);              \
            }                                                              \
        }                                                                  \
    }                                                                      \
                                                                           \
    /*                                                                     \
     *  Make sure we don't need to get the linguistic tables.              \
     */                                                                    \
    if ((dwFlags) && (pLocHashN) && (pLocHashN->pLowerLinguist == NULL))   \
    {                                                                      \
        /*                                                                 \
         *  Get locale hash node to make sure the appropriate              \
         *  casing and sorting tables are in the system.                   \
         */                                                                \
        pLocHashN = GetLangHashNode(Locale, dwFlags);                      \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  VALIDATE_LOCALE
//
//  Checks that the given LCID contains a valid locale id.  It does so
//  by making sure the appropriate locale information is present.  If the
//  locale is valid, pLocHashN will be non-NULL.  Otherwise, pLocHashN
//  will be NULL.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define VALIDATE_LOCALE(Locale, pLocHashN, UseCachedLocaleId)              \
{                                                                          \
    /*                                                                     \
     *  Check the system locale first for speed.  This is the most         \
     *  likely one to be used.                                             \
     */                                                                    \
    if (Locale == gSystemLocale)                                           \
    {                                                                      \
        pLocHashN = gpSysLocHashN;                                         \
    }                                                                      \
    /*                                                                     \
     *  Check the invariant locale second for speed.  This is the second   \
     *  most likely one to be used.                                        \
     */                                                                    \
    else if (Locale == LOCALE_INVARIANT)                                   \
    {                                                                      \
        pLocHashN = gpInvLocHashN;                                         \
    }                                                                      \
    else                                                                   \
    {                                                                      \
        /*                                                                 \
         *  Check special locale values.                                   \
         */                                                                \
        CHECK_SPECIAL_LOCALES(Locale, UseCachedLocaleId);                  \
                                                                           \
        /*                                                                 \
         *  If the locale is the system default, then the hash node        \
         *  is already stored in a global.                                 \
         */                                                                \
        if (Locale == gSystemLocale)                                       \
        {                                                                  \
            pLocHashN = gpSysLocHashN;                                     \
        }                                                                  \
        else if (IS_INVALID_LOCALE(Locale))                                \
        {                                                                  \
            pLocHashN = NULL;                                              \
        }                                                                  \
        else                                                               \
        {                                                                  \
            /*                                                             \
             *  Get locale hash node to make sure the appropriate          \
             *  locale table is in the system.                             \
             *                                                             \
             *  NOTE:  If the call fails, pLocHashN will be NULL.          \
             */                                                            \
            pLocHashN = GetLocHashNode(Locale);                            \
        }                                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  OPEN_CODEPAGE_KEY
//
//  Opens the key for the code page section of the registry for read access.
//
//  DEFINED AS A MACRO.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define OPEN_CODEPAGE_KEY(ReturnVal)                                       \
{                                                                          \
    /*                                                                     \
     *  Make sure code page key is open.                                   \
     */                                                                    \
    if (hCodePageKey == NULL)                                              \
    {                                                                      \
        RtlEnterCriticalSection(&gcsTblPtrs);                              \
        if (hCodePageKey == NULL)                                          \
        {                                                                  \
            if (OpenRegKey( &hCodePageKey,                                 \
                            NLS_HKLM_SYSTEM,                               \
                            NLS_CODEPAGE_KEY,                              \
                            KEY_READ ))                                    \
            {                                                              \
                SetLastError(ERROR_BADDB);                                 \
                RtlLeaveCriticalSection(&gcsTblPtrs);                      \
                return (ReturnVal);                                        \
            }                                                              \
        }                                                                  \
        RtlLeaveCriticalSection(&gcsTblPtrs);                              \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  OPEN_LOCALE_KEY
//
//  Opens the key for the locale section of the registry for read access.
//
//  DEFINED AS A MACRO.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define OPEN_LOCALE_KEY(ReturnVal)                                         \
{                                                                          \
    /*                                                                     \
     *  Make sure locale key is open.                                      \
     */                                                                    \
    if (hLocaleKey == NULL)                                                \
    {                                                                      \
        RtlEnterCriticalSection(&gcsTblPtrs);                              \
        if (hLocaleKey == NULL)                                            \
        {                                                                  \
            if (OpenRegKey( &hLocaleKey,                                   \
                            NLS_HKLM_SYSTEM,                               \
                            NLS_LOCALE_KEY,                                \
                            KEY_READ ))                                    \
            {                                                              \
                SetLastError(ERROR_BADDB);                                 \
                RtlLeaveCriticalSection(&gcsTblPtrs);                      \
                return (ReturnVal);                                        \
            }                                                              \
        }                                                                  \
        RtlLeaveCriticalSection(&gcsTblPtrs);                              \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  OPEN_ALT_SORTS_KEY
//
//  Opens the key for the alternate sorts section of the registry for read
//  access.
//
//  DEFINED AS A MACRO.
//
//  11-15-96    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define OPEN_ALT_SORTS_KEY(ReturnVal)                                      \
{                                                                          \
    /*                                                                     \
     *  Make sure alternate sorts key is open.                             \
     */                                                                    \
    if (hAltSortsKey == NULL)                                              \
    {                                                                      \
        RtlEnterCriticalSection(&gcsTblPtrs);                              \
        if (hAltSortsKey == NULL)                                          \
        {                                                                  \
            if (OpenRegKey( &hAltSortsKey,                                 \
                            NLS_HKLM_SYSTEM,                               \
                            NLS_ALT_SORTS_KEY,                             \
                            KEY_READ ))                                    \
            {                                                              \
                SetLastError(ERROR_BADDB);                                 \
                RtlLeaveCriticalSection(&gcsTblPtrs);                      \
                return (ReturnVal);                                        \
            }                                                              \
        }                                                                  \
        RtlLeaveCriticalSection(&gcsTblPtrs);                              \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  OPEN_LANG_GROUPS_KEY
//
//  Opens the key for the language groups section of the registry for
//  read access.
//
//  DEFINED AS A MACRO.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define OPEN_LANG_GROUPS_KEY(ReturnVal)                                    \
{                                                                          \
    /*                                                                     \
     *  Make sure language groups key is open.                             \
     */                                                                    \
    if (hLangGroupsKey == NULL)                                            \
    {                                                                      \
        RtlEnterCriticalSection(&gcsTblPtrs);                              \
        if (hLangGroupsKey == NULL)                                        \
        {                                                                  \
            if (OpenRegKey( &hLangGroupsKey,                               \
                            NLS_HKLM_SYSTEM,                               \
                            NLS_LANGUAGE_GROUPS_KEY,                       \
                            KEY_READ ))                                    \
            {                                                              \
                SetLastError(ERROR_BADDB);                                 \
                RtlLeaveCriticalSection(&gcsTblPtrs);                      \
                return (ReturnVal);                                        \
            }                                                              \
        }                                                                  \
        RtlLeaveCriticalSection(&gcsTblPtrs);                              \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  OPEN_MUILANG_KEY
//
//  Opens the key for the multilingual UI language section of the registry
//  for read access.  It is acceptable if this key is not in the registry,
//  so do not call SetLastError if the key cannot be opened.
//
//  DEFINED AS A MACRO.
//
//  03-10-98    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define OPEN_MUILANG_KEY(hKey, ReturnVal)                                  \
{                                                                          \
    if ((hKey) == NULL)                                                    \
    {                                                                      \
        if (OpenRegKey( &(hKey),                                           \
                        NLS_HKLM_SYSTEM,                                   \
                        NLS_MUILANG_KEY,                                   \
                        KEY_READ ))                                        \
        {                                                                  \
            return (ReturnVal);                                            \
        }                                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  OPEN_CPANEL_INTL_KEY
//
//  Opens the key for the control panel international section of the
//  registry for the given access.
//
//  DEFINED AS A MACRO.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define OPEN_CPANEL_INTL_KEY(hKey, ReturnVal, Access)                      \
{                                                                          \
    if ((hKey) == NULL)                                                    \
    {                                                                      \
        if (OpenRegKey( &(hKey),                                           \
                        NULL,                                              \
                        NLS_CTRL_PANEL_KEY,                                \
                        Access ))                                          \
        {                                                                  \
            SetLastError(ERROR_BADDB);                                     \
            return (ReturnVal);                                            \
        }                                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  OPEN_GEO_KEY
//
//  Opens the key for the geographic information section of the registry
//  for read access.
//
//  DEFINED AS A MACRO.
//
//  03-10-00    lguindon    Created.
////////////////////////////////////////////////////////////////////////////

#define OPEN_GEO_KEY(hKey, ReturnVal, Access)                              \
{                                                                          \
    if ((hKey) == NULL)                                                    \
    {                                                                      \
        if (OpenRegKey( &(hKey),                                           \
                        NULL,                                              \
                        GEO_REG_KEY,                                       \
                        Access ))                                          \
        {                                                                  \
            SetLastError(ERROR_BADDB);                                     \
            return (ReturnVal);                                            \
        }                                                                  \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  CLOSE_REG_KEY
//
//  Closes the given registry key.
//
//  DEFINED AS A MACRO.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define CLOSE_REG_KEY(hKey)                                                \
{                                                                          \
    if ((hKey) != NULL)                                                    \
    {                                                                      \
        NtClose(hKey);                                                     \
        hKey = NULL;                                                       \
    }                                                                      \
}


////////////////////////////////////////////////////////////////////////////
//
//  NLS_ALLOC_MEM
//
//  Allocates the given number of bytes of memory from the process heap,
//  zeros the memory buffer, and returns the handle.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define NLS_ALLOC_MEM(dwBytes)                                             \
    ( RtlAllocateHeap( RtlProcessHeap(),                                   \
                       HEAP_ZERO_MEMORY,                                   \
                       dwBytes ) )


////////////////////////////////////////////////////////////////////////////
//
//  NLS_FREE_MEM
//
//  Frees the memory of the given handle from the process heap.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define NLS_FREE_MEM(hMem)                                                 \
    ( (hMem) ? (RtlFreeHeap( RtlProcessHeap(),                             \
                             0,                                            \
                             (PVOID)hMem ))                                \
             : 0 )


////////////////////////////////////////////////////////////////////////////
//
//  NLS_FREE_TMP_BUFFER
//
//  Checks to see if the buffer is the same as the static buffer.  If it
//  is NOT the same, then the buffer is freed.
//
//  11-04-92    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define NLS_FREE_TMP_BUFFER(pBuf, pStaticBuf)                              \
{                                                                          \
    if (pBuf != pStaticBuf)                                                \
    {                                                                      \
        NLS_FREE_MEM(pBuf);                                                \
    }                                                                      \
}

////////////////////////////////////////////////////////////////////////////
//
//  ARRAYSIZE
//
//  Hnady utility macro to get the size of an array (such as an array of
//  WCHARs).
////////////////////////////////////////////////////////////////////////////
#ifndef ARRAYSIZE
#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
#endif



////////////////////////////////////////////////////////////////////////////
//
//  Function Prototypes
//
////////////////////////////////////////////////////////////////////////////

//
//  Table Routines - tables.c.
//
ULONG
AllocTables(void);

ULONG
GetUnicodeFileInfo(void);

ULONG
GetGeoFileInfo(void);

ULONG
GetCTypeFileInfo(void);

ULONG
GetDefaultSortkeyFileInfo(void);

ULONG
GetDefaultSortTablesFileInfo(void);

ULONG
GetSortkeyFileInfo(
    LCID Locale,
    PLOC_HASH pHashN);

void
GetSortTablesFileInfo(
    LCID Locale,
    PLOC_HASH pHashN);

ULONG
GetCodePageFileInfo(
    UINT CodePage,
    PCP_HASH *ppNode);

ULONG
GetLanguageFileInfo(
    LCID Locale,
    PLOC_HASH *ppNode,
    BOOLEAN fCreateNode,
    DWORD dwFlags);

ULONG
GetLocaleFileInfo(
    LCID Locale,
    PLOC_HASH *ppNode,
    BOOLEAN fCreateNode);

ULONG
MakeCPHashNode(
    UINT CodePage,
    LPWORD pBaseAddr,
    PCP_HASH *ppNode,
    BOOL IsDLL,
    LPFN_CP_PROC pfnCPProc);

ULONG
MakeLangHashNode(
    LCID Locale,
    LPWORD pBaseAddr,
    PLOC_HASH *ppNode,
    BOOLEAN fCreateNode);

ULONG
MakeLocHashNode(
    LCID Locale,
    LPWORD pBaseAddr,
    PLOC_HASH *ppNode,
    BOOLEAN fCreateNode);

PCP_HASH FASTCALL
GetCPHashNode(
    UINT CodePage);

PLOC_HASH FASTCALL
GetLangHashNode(
    LCID Locale,
    DWORD dwFlags);

BOOL
IsCPHashNodeLoaded(
    UINT CodePage);

PLOC_HASH FASTCALL
GetLocHashNode(
    LCID Locale);

ULONG
GetCalendar(
    CALID Calendar,
    PCAL_INFO *ppCalInfo);


//
//  Section Routines - section.c.
//
ULONG
CreateNlsObjectDirectory(void);

ULONG
CreateRegKey(
    PHANDLE phKeyHandle,
    LPWSTR pBaseName,
    LPWSTR pKey,
    ULONG fAccess);

ULONG
OpenRegKey(
    PHANDLE phKeyHandle,
    LPWSTR pBaseName,
    LPWSTR pKey,
    ULONG fAccess);

ULONG
QueryRegValue(
    HANDLE hKeyHandle,
    LPWSTR pValue,
    PKEY_VALUE_FULL_INFORMATION *ppKeyValueFull,
    ULONG Length,
    LPBOOL pIfAlloc);

ULONG
SetRegValue(
    HANDLE hKeyHandle,
    LPCWSTR pValue,
    LPCWSTR pData,
    ULONG DataLength);

ULONG
CreateSectionTemp(
    HANDLE *phSec,
    LPWSTR pwszFileName);

ULONG
OpenSection(
    HANDLE *phSec,
    PUNICODE_STRING pObSectionName,
    PVOID *ppBaseAddr,
    ULONG AccessMask,
    BOOL bCloseHandle);

ULONG
MapSection(
    HANDLE hSec,
    PVOID *ppBaseAddr,
    ULONG PageProtection,
    BOOL bCloseHandle);

ULONG
UnMapSection(
    PVOID pBaseAddr);

ULONG
GetNlsSectionName(
    UINT Value,
    UINT Base,
    UINT Padding,
    LPWSTR pwszPrefix,
    LPWSTR pwszSecName,
    UINT cchSecName);

ULONG
GetCodePageDLLPathName(
    UINT CodePage,
    LPWSTR pDllName,
    USHORT cchLen);


//
//  Utility Routines - util.c.
//
BOOL GetCPFileNameFromRegistry(
    UINT    CodePage,
    LPWSTR  pResultBuf,
    UINT    Size);
  
BOOL GetUserInfoFromRegistry(
    LPWSTR pValue,
    LPWSTR pOutput,
    LCID Locale);

int
GetStringTableEntry(
    UINT ResourceID,
    LANGID UILangId,
    LPWSTR pBuffer,
    int cchBuffer,
    int WhichString);

BOOL
IsValidSeparatorString(
    LPCWSTR pString,
    ULONG MaxLength,
    BOOL fCheckZeroLen);

BOOL
IsValidGroupingString(
    LPCWSTR pString,
    ULONG MaxLength,
    BOOL fCheckZeroLen);

LPWORD
IsValidCalendarType(
    PLOC_HASH pHashN,
    CALID CalId);

LPWORD
IsValidCalendarTypeStr(
    PLOC_HASH pHashN,
    LPCWSTR pCalStr);

BOOL
GetUserInfo(
    LCID Locale,
    LCTYPE LCType ,
    SIZE_T CacheOffset,
    LPWSTR pValue,
    LPWSTR pOutput,
    size_t cchOutput,
    BOOL fCheckNull);

WCHAR FASTCALL
GetPreComposedChar(
    WCHAR wcNonSp,
    WCHAR wcBase);

BOOL FASTCALL
GetCompositeChars(
    WCHAR wch,
    WCHAR *pNonSp,
    WCHAR *pBase);

int FASTCALL
InsertPreComposedForm(
    LPCWSTR pWCStr,
    LPWSTR pEndWCStr,
    LPWSTR pPreComp);

int FASTCALL
InsertFullWidthPreComposedForm(
    LPCWSTR pWCStr,
    LPWSTR pEndWCStr,
    LPWSTR pPreComp,
    PCASE pCase);

int FASTCALL
InsertCompositeForm(
    LPWSTR pWCStr,
    LPWSTR pEndWCStr);

ULONG
NlsConvertIntegerToString(
    UINT Value,
    UINT Base,
    UINT Padding,
    LPWSTR pResultBuf,
    UINT Size);

BOOL FASTCALL
NlsConvertIntegerToHexStringW(
    UINT Value,
    BOOL UpperCase,
    PWSTR Str,
    UINT Width);

BOOL FASTCALL
NlsConvertStringToIntegerW(
    PWSTR str,
    UINT Base,
    int CharCount,
    UINT* Result);

BOOL FASTCALL
NlsIsDll(
    LPCWSTR pFileName);

LPWSTR FASTCALL
NlsStrCpyW(
    LPWSTR pwszDest,
    LPCWSTR pwszSrc);

LPWSTR FASTCALL
NlsStrCatW(
    LPWSTR pwsz1,
    LPCWSTR pwsz2);

int FASTCALL
NlsStrLenW(
    LPCWSTR pwsz);

LPWSTR FASTCALL
NlsStrNCatW(
    LPWSTR pwszFront,
    LPCWSTR pwszBack,
    int Count);

int FASTCALL
NlsStrEqualW(
    LPCWSTR pwszFirst,
    LPCWSTR pwszSecond);

int FASTCALL
NlsStrNEqualW(
    LPCWSTR pwszFirst,
    LPCWSTR pwszSecond,
    int Count);

//
//  Security Routines - security.c.
//

NTSTATUS
NlsCheckForInteractiveUser();

NTSTATUS
NlsIsInteractiveUserProcess();

NTSTATUS
NlsGetUserLocale(
    LCID *Lcid);

NTSTATUS
NlsGetCurrentUserNlsInfo(
    LCID Locale,
    LCTYPE LCType,
    PWSTR RegistryValue,
    PWSTR pOutputBuffer,
    BOOL IgnoreLocaleValue);


NTSTATUS
NlsQueryCurrentUserInfo(
    PNLS_LOCAL_CACHE pNlsCache,
    LPWSTR pValue,
    LPWSTR pOutput);

NTSTATUS
NlsFlushProcessCache(
    LCTYPE LCType);


//
//  Internal Enumeration routines - enum.c.
//
BOOL
Internal_EnumSystemLanguageGroups(
    NLS_ENUMPROC lpLanguageGroupEnumProc,
    DWORD dwFlags,
    LONG_PTR lParam,
    BOOL fUnicodeVer);

BOOL
Internal_EnumLanguageGroupLocales(
    NLS_ENUMPROC lpLangGroupLocaleEnumProc,
    LGRPID LanguageGroup,
    DWORD dwFlags,
    LONG_PTR lParam,
    BOOL fUnicodeVer);

BOOL
Internal_EnumUILanguages(
    NLS_ENUMPROC lpUILanguageEnumProc,
    DWORD dwFlags,
    LONG_PTR lParam,
    BOOL fUnicodeVer);

BOOL
Internal_EnumSystemLocales(
    NLS_ENUMPROC lpLocaleEnumProc,
    DWORD dwFlags,
    BOOL fUnicodeVer);

BOOL
Internal_EnumSystemCodePages(
    NLS_ENUMPROC lpCodePageEnumProc,
    DWORD dwFlags,
    BOOL fUnicodeVer);

BOOL
Internal_EnumCalendarInfo(
    NLS_ENUMPROC lpCalInfoEnumProc,
    LCID Locale,
    CALID Calendar,
    CALTYPE CalType,
    BOOL fUnicodeVer,
    BOOL fExVersion);

BOOL
Internal_EnumTimeFormats(
    NLS_ENUMPROC lpTimeFmtEnumProc,
    LCID Locale,
    DWORD dwFlags,
    BOOL fUnicodeVer);

BOOL
Internal_EnumDateFormats(
    NLS_ENUMPROC lpDateFmtEnumProc,
    LCID Locale,
    DWORD dwFlags,
    BOOL fUnicodeVer,
    BOOL fExVersion);


//
//  Ansi routines - ansi.c.
//
BOOL
NlsDispatchAnsiEnumProc(
    LCID Locale,
    NLS_ENUMPROC pNlsEnumProc,
    DWORD dwFlags,
    LPWSTR pUnicodeBuffer1,
    LPWSTR pUnicodeBuffer2,
    DWORD dwValue1,
    DWORD dwValue2,
    LONG_PTR lParam,
    BOOL fVersion);


//
//  Translation Routines - mbcs.c.
//
int
SpecialMBToWC(
    PCP_HASH pHashN,
    DWORD dwFlags,
    LPCSTR lpMultiByteStr,
    int cbMultiByte,
    LPWSTR lpWideCharStr,
    int cchWideChar);


//
//  UTF Translation Routines - utf.c.
//
BOOL
UTFCPInfo(
    UINT CodePage,
    LPCPINFO lpCPInfo,
    BOOL fExVer);

int
UTFToUnicode(
    UINT CodePage,
    DWORD dwFlags,
    LPCSTR lpMultiByteStr,
    int cbMultiByte,
    LPWSTR lpWideCharStr,
    int cchWideChar);

int
UnicodeToUTF(
    UINT CodePage,
    DWORD dwFlags,
    LPCWSTR lpWideCharStr,
    int cchWideChar,
    LPSTR lpMultiByteStr,
    int cbMultiByte,
    LPCSTR lpDefaultChar,
    LPBOOL lpUsedDefaultChar);


//
// Locale/Calendar Info (locale.c)
//
BOOL
GetTwoDigitYearInfo(
    CALID Calendar,
    LPWSTR pYearInfo,
    PWSTR pwszKeyPath);





////////////////////////////////////////////////////////////////////////////
//
//  Global Variables
//
//  All of the global variables for the NLSAPI should be put here.  These are
//  all instance-specific.  In general, there shouldn't be much reason to
//  create instance globals.
//
//  Globals are included last because they may require some of the types
//  being defined above.
//
////////////////////////////////////////////////////////////////////////////

extern PTBL_PTRS        pTblPtrs;           // ptr to structure of table ptrs
extern HANDLE           hModule;            // handle to module
extern RTL_CRITICAL_SECTION gcsTblPtrs;     // critical section for tbl ptrs

extern UINT             gAnsiCodePage;      // Ansi code page value
extern UINT             gOemCodePage;       // OEM code page value
extern UINT             gMacCodePage;       // MAC code page value
extern LCID             gSystemLocale;      // system locale value
extern LANGID           gSystemInstallLang; // system's original install language
extern PLOC_HASH        gpSysLocHashN;      // ptr to system loc hash node
extern PLOC_HASH        gpInvLocHashN;      // ptr to invariant loc hash node
extern PCP_HASH         gpACPHashN;         // ptr to ACP hash node
extern PCP_HASH         gpOEMCPHashN;       // ptr to OEMCP hash node
extern PCP_HASH         gpMACCPHashN;       // ptr to MAC hash node

extern HANDLE           hCodePageKey;       // handle to System\Nls\CodePage key
extern HANDLE           hLocaleKey;         // handle to System\Nls\Locale key
extern HANDLE           hAltSortsKey;       // handle to Locale\Alternate Sorts key
extern HANDLE           hLangGroupsKey;     // handle to System\Nls\Language Groups key

extern PNLS_USER_INFO   pNlsUserInfo;       // ptr to the user info cache

extern BOOL gInteractiveLogonUserProcess;    // running in interactive user session or not.
extern RTL_CRITICAL_SECTION  gcsNlsProcessCache; // Nls process cache critical section





////////////////////////////////////////////////////////////////////////////
//
//   Functions used to communicate with CSRSS.
//
////////////////////////////////////////////////////////////////////////////

NTSTATUS
CsrBasepNlsGetUserInfo(
    IN LCID Locale,
    IN SIZE_T CacheOffset,
    OUT LPWSTR pData,
    IN ULONG DataLength);


NTSTATUS
CsrBasepNlsSetUserInfo(
    IN LCTYPE LCType,
    IN LPWSTR pData,
    IN ULONG DataLength);

NTSTATUS
CsrBasepNlsSetMultipleUserInfo(
    IN DWORD dwFlags,
    IN int cchData,
    IN LPCWSTR pPicture,
    IN LPCWSTR pSeparator,
    IN LPCWSTR pOrder,
    IN LPCWSTR pTLZero,
    IN LPCWSTR pTimeMarkPosn);

NTSTATUS
CsrBasepNlsCreateSection(
    IN UINT uiType,
    IN LCID Locale,
    OUT PHANDLE phSection);

NTSTATUS
CsrBasepNlsUpdateCacheCount(VOID);

#endif   // _NLS_
