/*++

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

Module Name:

    init.c

Abstract:

    This file contains the initialization code for the NLS APIs.

    External Routines found in this file:
      NlsDllInitialize

Revision History:

    05-31-91    JulieB    Created.

--*/



//
//  Include Files.
//

#include "nls.h"
#include "stdio.h"



//
//  Global Variables.
//

HANDLE                hModule;              // handle to module
RTL_CRITICAL_SECTION  gcsTblPtrs;           // critical section for tbl ptrs

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

HANDLE                hCodePageKey;         // handle to System\Nls\CodePage key
HANDLE                hLocaleKey;           // handle to System\Nls\Locale key
HANDLE                hAltSortsKey;         // handle to Locale\Alternate Sorts key
HANDLE                hLangGroupsKey;       // handle to System\Nls\Language Groups key
PNLS_USER_INFO        pNlsUserInfo;         // ptr to the user info cache

RTL_CRITICAL_SECTION  gcsNlsProcessCache;   // critical section for nls process cache




//
//  Forward Declarations.
//

ULONG
NlsServerInitialize(void);

ULONG
NlsProcessInitialize(void);

void
InitKoreanWeights(void);





//-------------------------------------------------------------------------//
//                           EXTERNAL ROUTINES                             //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  NlsDllInitialize
//
//  DLL Entry initialization procedure for NLSAPI.  This is called by
//  the base dll initialization.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOLEAN NlsDllInitialize(
    IN PVOID hMod,
    ULONG Reason,
    IN PBASE_STATIC_SERVER_DATA pBaseStaticServerData)
{

    if (Reason == DLL_PROCESS_ATTACH)
    {
        ULONG rc;

        //
        //  Save module handle for use later.
        //
        hModule = (HANDLE)hMod;

        //
        //  Initialize the cached user info pointer.
        //
        pNlsUserInfo = &(pBaseStaticServerData->NlsUserInfo);

        //
        //  Process attaching, so initialize tables.
        //
        rc = NlsServerInitialize();
        if (rc)
        {
            KdPrint(("NLSAPI: Could NOT initialize Server - %lx.\n", rc));
            return (FALSE);
        }

        rc = NlsProcessInitialize();
        if (rc)
        {
            KdPrint(("NLSAPI: Could NOT initialize Process - %lx.\n", rc));
            return (FALSE);
        }
    }

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


////////////////////////////////////////////////////////////////////////////
//
//  NlsThreadCleanup
//
//  Cleanup for thread resources when it terminates.
//
//  03-30-99    SamerA    Created.
////////////////////////////////////////////////////////////////////////////

BOOLEAN NlsThreadCleanup(void)
{
    if (NtCurrentTeb()->NlsCache)
    {
        CLOSE_REG_KEY( ((PNLS_LOCAL_CACHE)NtCurrentTeb()->NlsCache)->CurrentUserKeyHandle );
        RtlFreeHeap( RtlProcessHeap(),
                     0,
                     NtCurrentTeb()->NlsCache
                   );
    }

    return (TRUE);
}





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

////////////////////////////////////////////////////////////////////////////
//
//  NlsServerInitialize
//
//  Server initialization procedure for NLSAPI.  This is the ONE-TIME
//  initialization code for the NLSAPI DLL.  It simply does the calls
//  to NtCreateSection for the code pages that are currently found in the
//  system.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG NlsServerInitialize(void)
{
    HANDLE hSec = (HANDLE)0;           // section handle
    ULONG rc = 0L;                     // return code

#ifndef DOSWIN32
    PIMAGE_NT_HEADERS NtHeaders;

    //
    //  This is to avoid being initialized again when NTSD dynlinks to
    //  a server to get at its debugger extensions.
    //
    NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
    if (NtHeaders &&
        (NtHeaders->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE))
    {
        return (NO_ERROR);
    }
#endif

    //
    //  MultiUser NT (Hydra). SesssionId = 0 is the console CSRSS.
    //  If this is NOT the first server process, then just return success,
    //  since we only want to create the object directories once.
    //
    if (NtCurrentPeb()->SessionId != 0)
    {
        return (NO_ERROR);
    }

    //
    //  Create the NLS object directory.
    //
    //  Must create a separate directory off the root in order to have
    //  CreateSection access on the fly.
    //
    if (rc = CreateNlsObjectDirectory())
    {
        return (rc);
    }

    //
    //  The ACP, OEMCP, and Default Language files are already created
    //  at boot time.  The pointers to the files are stored in the PEB.
    //
    //  Create the section for the following data files:
    //      UNICODE
    //      LOCALE
    //      CTYPE
    //      SORTKEY
    //      SORT TABLES
    //
    //  All other data files will have the sections created only as they
    //  are needed.
    //
    if ((!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_UNICODE, 0, &hSec))) ||
        (!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_LOCALE, 0, &hSec)))  ||
        (!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_CTYPE, 0, &hSec)))   ||
        (!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_SORTKEY, 0, &hSec))) ||
        (!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_SORTTBLS, 0, &hSec))))
    {
        return (rc);
    }

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


////////////////////////////////////////////////////////////////////////////
//
//  NlsProcessInitialize
//
//  Process initialization procedure for NLS API.  This routine sets up all
//  of the tables so that they are accessable from the current process.  If
//  it is unable to allocate the appropriate memory or memory map the
//  appropriate files, an error is returned.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG NlsProcessInitialize(void)
{
    ULONG rc = 0L;                     // return code
    LPWORD pBaseAddr;                  // ptr to base address of section
    LCID UserLocale;                   // user locale id
    PLOC_HASH pUserLocHashN;           // ptr to user locale hash node

    //
    //  Initialize the critical section that protects the NLS cache for
    //  this process.
    //
    if ((rc = RtlInitializeCriticalSection(&gcsNlsProcessCache)) != ERROR_SUCCESS)
    {
        return (rc);
    }

    //
    //  Initialize the table pointers critical section.
    //  Enter the critical section to set up the tables.
    //
    if ((rc = RtlInitializeCriticalSectionAndSpinCount(&gcsTblPtrs, 4000)) != ERROR_SUCCESS)
    {
        return (rc);
    }
    RtlEnterCriticalSection(&gcsTblPtrs);

    //
    //  Allocate initial tables.
    //
    if (rc = AllocTables())
    {
        KdPrint(("AllocTables failed, rc %lx\n", rc));
        RtlLeaveCriticalSection(&gcsTblPtrs);
        return (rc);
    }

    //
    //  Initialize the handles to the various registry keys to NULL.
    //
    hCodePageKey = NULL;
    hLocaleKey = NULL;
    hAltSortsKey = NULL;
    hLangGroupsKey = NULL;

    //
    //  Get the ANSI code page value.
    //  Create the hash node for the ACP.
    //  Insert the hash node into the global CP hash table.
    //
    //  At this point, the ACP table has already been mapped into
    //  the process, so get the pointer from the PEB.
    //
    pBaseAddr = NtCurrentPeb()->AnsiCodePageData;
    gAnsiCodePage = ((PCP_TABLE)(pBaseAddr + CP_HEADER))->CodePage;
    if (rc = MakeCPHashNode( gAnsiCodePage,
                             pBaseAddr,
                             &gpACPHashN,
                             FALSE,
                             NULL ))
    {
        RtlLeaveCriticalSection(&gcsTblPtrs);
        return (rc);
    }

    //
    //  Get the OEM code page value.
    //  Create the hash node for the OEMCP.
    //  Insert the hash node into the global CP hash table.
    //
    //  At this point, the OEMCP table has already been mapped into
    //  the process, so get the pointer from the PEB.
    //
    pBaseAddr = NtCurrentPeb()->OemCodePageData;
    gOemCodePage = ((PCP_TABLE)(pBaseAddr + CP_HEADER))->CodePage;
    if (gOemCodePage != gAnsiCodePage)
    {
        //
        //  Oem code page is different than the Ansi code page, so
        //  need to create and store the new hash node.
        //
        if (rc = MakeCPHashNode( gOemCodePage,
                                 pBaseAddr,
                                 &gpOEMCPHashN,
                                 FALSE,
                                 NULL ))
        {
            RtlLeaveCriticalSection(&gcsTblPtrs);
            return (rc);
        }
    }
    else
    {
        //
        //  Oem code page is the same as the Ansi code page, so set
        //  the oem cp hash node to be the same as the ansi cp hash node.
        //
        gpOEMCPHashN = gpACPHashN;
    }

    //
    //  Initialize the MAC code page values to 0.
    //  These values will be set the first time they are requested for use.
    //
    gMacCodePage = 0;
    gpMACCPHashN = NULL;

    //
    //  Open and Map a View of the Section for UNICODE.NLS.
    //  Save the pointers to the table information in the table ptrs
    //  structure.
    //
    if (rc = GetUnicodeFileInfo())
    {
        KdPrint(("GetUnicodeFileInfo failed, rc %lx\n", rc));
        RtlLeaveCriticalSection(&gcsTblPtrs);
        return (rc);
    }

    //
    //  Cache the system locale value.
    //
    rc = NtQueryDefaultLocale(FALSE, &gSystemLocale);
    if (!NT_SUCCESS(rc))
    {
        RtlLeaveCriticalSection(&gcsTblPtrs);
        return (rc);
    }

    //
    //  Store the user locale value.
    //
    UserLocale = pNlsUserInfo->UserLocaleId;
    if (UserLocale == 0)        
    {
        UserLocale = gSystemLocale;
    }

    //
    //  Initialize the system install language to zero.  This will only
    //  be retrieved on an as need basis.
    //
    gSystemInstallLang = 0;

    //
    //  Open and Map a View of the Section for LOCALE.NLS.
    //  Create and insert the hash node into the global Locale hash table
    //  for the system default locale.
    //
    if (rc = GetLocaleFileInfo( gSystemLocale,
                                &gpSysLocHashN,
                                TRUE ))
    {
        //
        //  Change the system locale to be the default (English).
        //
        if (GetLocaleFileInfo( MAKELCID(NLS_DEFAULT_LANGID, SORT_DEFAULT),
                               &gpSysLocHashN,
                               TRUE ))
        {
            KdPrint(("Couldn't do English\n"));
            RtlLeaveCriticalSection(&gcsTblPtrs);
            return (rc);
        }
        else
        {
            //
            //  Registry is corrupt, but allow the English default to
            //  work.  Need to reset the system default.
            //
            gSystemLocale = MAKELCID(NLS_DEFAULT_LANGID, SORT_DEFAULT);
            KdPrint(("NLSAPI: Registry is corrupt - Using Default Locale.\n"));
        }
    }

    //
    //  If the user default locale is different from the system default
    //  locale, then create and insert the hash node into the global
    //  Locale hash table for the user default locale.
    //
    //  NOTE:  The System Default Locale Hash Node should be
    //         created before this call.
    //
    if (UserLocale != gSystemLocale)
    {
        if (rc = GetLocaleFileInfo( UserLocale,
                                    &pUserLocHashN,
                                    TRUE ))
        {
            //
            //  Change the user locale to be equal to the system default.
            //
            UserLocale = gSystemLocale;
            KdPrint(("NLSAPI: Registry is corrupt - User Locale Now Equals System Locale.\n"));
        }
    }

    //
    //  Create and insert the hash node into the global Locale hash
    //  table for the invariant locale.
    //
    if (rc = GetLocaleFileInfo( LOCALE_INVARIANT,
                                &gpInvLocHashN,
                                TRUE ))
    {
        KdPrint(("NLSAPI: Registry is corrupt - Invariant Locale Cannot Be Initialized.\n"));
    }

    //
    //  Open and Map a View of the Section for SORTKEY.NLS.
    //  Save the pointers to the semaphore dword and the default sortkey
    //  table in the table ptrs structure.
    //
    if (rc = GetDefaultSortkeyFileInfo())
    {
        KdPrint(("NLSAPI: Initialization, GetDefaultSortkeyFileInfo failed with rc %lx.\n", rc));
//      RtlLeaveCriticalSection(&gcsTblPtrs);
//      return (rc);
    }

    //
    //  Open and Map a View of the Section for SORTTBLS.NLS.
    //  Save the pointers to the sort table information in the
    //  table ptrs structure.
    //
    if (rc = GetDefaultSortTablesFileInfo())
    {
        RtlLeaveCriticalSection(&gcsTblPtrs);
        return (rc);
    }

    //
    //  Get the language information portion of the system locale.
    //
    //  NOTE:  GetDefaultSortkeyFileInfo and GetDefaultSortTablesFileInfo
    //         should be called before this so that the default sorting
    //         tables are already initialized at the time of the call.
    //
    if (rc = GetLanguageFileInfo( gSystemLocale,
                                  &gpSysLocHashN,
                                  FALSE,
                                  0 ))
    {
        RtlLeaveCriticalSection(&gcsTblPtrs);
        return (rc);
    }

    //
    //  Get the language information portion of the invariant locale. We
    //  use the default locale (US English).
    //
    if (rc = GetLanguageFileInfo( MAKELCID(NLS_DEFAULT_LANGID, SORT_DEFAULT),
                                  &gpInvLocHashN,
                                  FALSE,
                                  0 ))
    {
        RtlLeaveCriticalSection(&gcsTblPtrs);
        return (rc);
    }

    //
    //  If the user default is different from the system default,
    //  get the language information portion of the user default locale.
    //
    //  NOTE:  GetDefaultSortkeyFileInfo and GetDefaultSortTablesFileInfo
    //         should be called before this so that the default sorting
    //         tables are already initialized at the time of the call.
    //
    if (gSystemLocale != UserLocale)
    {
        if (rc = MakeLangHashNode( UserLocale,
                                   NULL,
                                   &pUserLocHashN,
                                   FALSE ))
        {
            RtlLeaveCriticalSection(&gcsTblPtrs);
            return (rc);
        }
    }

    //
    //  Initialize the Korean SMWeight values.
    //
    InitKoreanWeights();

    //
    //  Leave the critical section.
    //
    RtlLeaveCriticalSection(&gcsTblPtrs);

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


////////////////////////////////////////////////////////////////////////////
//
//  InitKoreanWeights
//
//  Creates the SMWeight array with the IDEOGRAPH script member sorting
//  before all other script members.
//
//  NOTE: This function assumes we're in a critical section.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

void InitKoreanWeights()
{
    DWORD ctr;                                 // loop counter
    BYTE NewScript;                            // new script to store
    LPBYTE pSMWeight = pTblPtrs->SMWeight;     // ptr to script member weights
    PMULTI_WT pMulti;                          // ptr to multi weight


    //
    //  Set the 0 to FIRST_SCRIPT of script structure to its default
    //  value.
    //
    RtlZeroMemory(pSMWeight, NUM_SM);
    for (ctr = 1; ctr < FIRST_SCRIPT; ctr++)
    {
        pSMWeight[ctr] = (BYTE)ctr;
    }

    //
    //  Save the order in the SMWeight array.
    //
    NewScript = FIRST_SCRIPT;
    pSMWeight[IDEOGRAPH] = NewScript;
    NewScript++;

    //
    //  See if the script is part of a multiple weights script.
    //
    pMulti = pTblPtrs->pMultiWeight;
    for (ctr = pTblPtrs->NumMultiWeight; ctr > 0; ctr--, pMulti++)
    {
        if (pMulti->FirstSM == IDEOGRAPH)
        {
            //
            //  Part of multiple weight, so must move entire range
            //  by setting each value in range to NewScript and
            //  then incrementing NewScript.
            //
            //  NOTE:  May use 'ctr' here since it ALWAYS breaks
            //         out of outer for loop.
            //
            for (ctr = 1; ctr < pMulti->NumSM; ctr++)
            {
                pSMWeight[IDEOGRAPH + ctr] = NewScript;
                NewScript++;
            }
            break;
        }
    }

    //
    //  Must set each script member that has not yet been reset to its
    //  new order.
    //
    //  The default ordering is to assign:
    //       Order  =  Script Member Value
    //
    //  Therefore, can simply set each zero entry in order to the end
    //  of the array to the next 'NewScript' value.
    //
    for (ctr = FIRST_SCRIPT; ctr < NUM_SM; ctr++)
    {
        //
        //  If it's a zero value, set it to the next sorting order value.
        //
        if (pSMWeight[ctr] == 0)
        {
            pSMWeight[ctr] = NewScript;
            NewScript++;
        }
    }
}
