#include "pch.h"
#include "loader.h"
#include <stdlib.h>
#pragma hdrstop


#define ISNT()      (g_VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
#define ISOSR2()    (LOWORD(g_VersionInfo.dwBuildNumber) > 1080)
#define BUILDNUM()  (g_VersionInfo.dwBuildNumber)

//
// Global variables defined here
//

//
// TargetNativeLangID : this is native language ID of running system
//
LANGID TargetNativeLangID;

//
// SourceNativeLangID : this is native language ID of new NT you want to install
//
LANGID SourceNativeLangID;

//
// g_IsLanguageMatched : if source and target language are matched (or compatible)
//
//                       1. if SourceNativeLangID == TargetNativeLangID
//
//                       2. if SourceNativeLangID's alternative ID == TargetNativeLangID
//
BOOL g_IsLanguageMatched;

typedef struct _tagAltSourceLocale {
    LANGID LangId;
    LANGID AltLangId;
    DWORD MajorOs;
    DWORD MinorOs;
    DWORD ExcludedOs;
} ALTSOURCELOCALE, *PALTSOURCELOCALE;

ALTSOURCELOCALE g_AltSourceLocale [] = {{0x00000C04, 0x00000409, 0x0200,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x0000040D, 0x00000409, 0x0200,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00000401, 0x00000409, 0x0200,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x0000041E, 0x00000409, 0x0200,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00000809, 0x00000409, 0x00FF,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x0000080A, 0x00000C0A, 0x00FF,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x0000040A, 0x00000C0A, 0x0300,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00000425, 0x00000409, 0x00FF,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00000801, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00000c01, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00001001, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00001401, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00001801, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00001c01, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00002001, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00002401, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00002801, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00002c01, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00003001, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00003401, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00003801, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00003c01, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0x00004001, 0x00000401, 0x0001,     0xFFFFFFFF, 0xFFFFFFFF},
                                        {0,          0,          0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}};

typedef struct _tagTrustedSourceLocale {
    LANGID LangId;
    DWORD MajorOs;
    DWORD MinorOs;
    DWORD ExcludedOs;
} TRUSTEDSOURCELOCALE, *PTRUSTEDSOURCELOCALE;

TRUSTEDSOURCELOCALE g_TrustedSourceLocale [] = {{0,          0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}};

typedef struct _tagOSVERSIONMAJORID {
    PCTSTR Name;
    DWORD MajorId;
    DWORD Platform;
    DWORD Major;
    DWORD Minor;
} OSVERSIONMAJORID, *POSVERSIONMAJORID;

OSVERSIONMAJORID g_OsVersionMajorId [] = {{TEXT("Win95"),       0x0001, 1, 4, 0},
                                          {TEXT("Win98"),       0x0002, 1, 4, 10},
                                          {TEXT("WinME"),       0x0004, 1, 4, 90},
                                          {TEXT("WinNT351"),    0x0100, 2, 3, 51},
                                          {TEXT("WinNT40"),     0x0200, 2, 4, 0},
                                          {NULL,                0,      0, 0, 0}};

typedef struct _tagOSVERSIONMINORID {
    PCTSTR Name;
    DWORD MajorId;
    DWORD MinorId;
    DWORD Platform;
    DWORD Major;
    DWORD Minor;
    DWORD Build;
    PCTSTR CSDVer;
} OSVERSIONMINORID, *POSVERSIONMINORID;

OSVERSIONMINORID g_OsVersionMinorId [] = {{NULL, 0, 0, 0, 0, 0, 0, NULL}};

typedef struct _tagLANGINFO {
    LANGID LangID;
    INT    Count;
} LANGINFO,*PLANGINFO;

BOOL
TrustedDefaultUserLocale (
    LANGID LangID
    );

BOOL
CALLBACK
EnumLangProc(
    HANDLE hModule,     // resource-module handle
    LPCTSTR lpszType,   // pointer to resource type
    LPCTSTR lpszName,   // pointer to resource name
    WORD wIDLanguage,   // resource language identifier
    LONG_PTR lParam     // application-defined parameter
    )
/*++

Routine Description:

    Callback that counts versions stamps.

Arguments:

    Details of version enumerated version stamp. (Ignore.)

Return Value:

    Indirectly thru lParam: count, langID

--*/
{
    PLANGINFO LangInfo;

    LangInfo = (PLANGINFO) lParam;

    LangInfo->Count++;

    //
    // for localized build contains multiple resource,
    // it usually contains 0409 as backup lang.
    //
    // if LangInfo->LangID != 0 means we already assigned an ID to it
    //
    // so when wIDLanguage == 0x409, we keep the one we got from last time
    //
    if ((wIDLanguage == 0x409) && (LangInfo->LangID != 0)) {
        return TRUE;
    }

    LangInfo->LangID  = wIDLanguage;

    return TRUE;        // continue enumeration
}

LANGID
GetNTDLLNativeLangID (
    VOID
    )
/*++

Routine Description:

    This function is designed specifically for getting native lang of ntdll.dll

    This is not a generic function to get other module's language

    the assumption is:

    1. if only one language in resource then return this lang

    2. if two languages in resource then return non-US language

    3. if more than two languages, it's invalid in our case, but returns the last one.

Arguments:

    None

Return Value:

    Native lang ID in ntdll.dll

--*/
{
    LPCTSTR Type = (LPCTSTR) RT_VERSION;
    LPCTSTR Name = (LPCTSTR) 1;

    LANGINFO LangInfo;

    ZeroMemory(&LangInfo,sizeof(LangInfo));

    EnumResourceLanguages (
            GetModuleHandle(TEXT("ntdll.dll")),
            Type,
            Name,
            EnumLangProc,
            (LONG_PTR) &LangInfo
            );

    if ((LangInfo.Count > 2) || (LangInfo.Count < 1) ) {
        //
        // put error log here
        //
        // so far, for NT 3.51, only JPN has two language resources
    }

    return LangInfo.LangID;
}

BOOL
IsHongKongVersion (
    VOID
    )
/*++

Routine Description:

    Try to identify HongKong NT 4.0

    It based on:

    NTDLL's language is English and build is 1381 and
    pImmReleaseContext return TRUE

Arguments:


Return Value:

   Language ID of running system

--*/
{
    HMODULE hMod;
    BOOL bRet=FALSE;
    typedef BOOL (*IMMRELEASECONTEXT) (HWND,HANDLE);
    IMMRELEASECONTEXT pImmReleaseContext;

    LANGID TmpID = GetNTDLLNativeLangID();

    if ((g_VersionInfo.dwBuildNumber == 1381) &&
        (TmpID == 0x0409)){

        hMod = LoadLibrary(TEXT("imm32.dll"));

        if (hMod) {

            pImmReleaseContext = (IMMRELEASECONTEXT) GetProcAddress(hMod,"ImmReleaseContext");

            if (pImmReleaseContext) {
                bRet = pImmReleaseContext(NULL,NULL);
            }

            FreeLibrary(hMod);
        }
    }
    return (bRet);
}

LANGID
GetDefaultUserLangID (
    VOID
    )
{
    LONG            dwErr;
    HKEY            hkey;
    DWORD           dwSize;
    CHAR            buffer[512];
    LANGID          langid = 0;

    dwErr = RegOpenKeyEx( HKEY_USERS,
                          TEXT(".DEFAULT\\Control Panel\\International"),
                          0,
                          KEY_READ,
                          &hkey );

    if( dwErr == ERROR_SUCCESS ) {

        dwSize = sizeof(buffer);
        dwErr = RegQueryValueExA(hkey,
                                 "Locale",
                                 NULL,  //reserved
                                 NULL,  //type
                                 buffer,
                                 &dwSize );

        if(dwErr == ERROR_SUCCESS) {
            langid = LANGIDFROMLCID(strtoul(buffer,NULL,16));

        }
        RegCloseKey(hkey);
    }
    return langid;
}

LANGID
GetTargetNativeLangID (
    VOID
    )
/*++

Routine Description:

    Applies different rules to different platforms

    NT
        build number <= 1840           : check ntdll's language,
                                         we scaned all 3.51's ntdll on boneyard\intl,
                                         it looks like we can trust them.
        build number > 1840            : user MUI language

    Win9x
        use default user's resource language

Arguments:


Return Value:

   Language ID of running system

--*/
{
    LONG            dwErr;
    HKEY            hkey;
    DWORD           dwSize;
    CHAR            buffer[512];
    LANGID          rcLang;
    LANGID          langid = 0;


    // Find out if we are running on NT or WIN9X

    if( ISNT() ) {

        //
        // We're on NT, but which version?  GetSystemDefaultUILanguage() was broke until 1840...
        //
        if( g_VersionInfo.dwBuildNumber > 1840 ) {
        FARPROC     NT5API;

            //
            // Use the API to find out our locale.
            //

            if( NT5API = GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetSystemDefaultUILanguage") ) {

                rcLang = (LANGID)NT5API();
                //
                // need to convert decimal to hex, LANGID to chr.
                //
                langid = rcLang;
            }
        } else {

                //
                // by looking into \\boneyard\intl, almost every ntdll.dll marked correct lang ID
                // so get langID from ntdll.dll
                //

                langid = GetNTDLLNativeLangID();

                if (langid == 0x0409) {

                    if (IsHongKongVersion()) {

                        langid = 0x0C04;

                    } else {
                        //
                        // if default user's locale is in [TrustedDefaultUserLocale]
                        //
                        // then this is a backdoor for some localized build that its ntdll.dll marked
                        //
                        // as English but can't be upgrade by US version.
                        //
                        LANGID DefaultUserLangID = GetDefaultUserLangID();

                        if (DefaultUserLangID  &&
                            TrustedDefaultUserLocale (DefaultUserLangID)) {

                            langid = DefaultUserLangID;
                        }
                    }
                }

        }
    } else {

        //
        // We're on Win9x.
        //
        dwErr = RegOpenKeyEx( HKEY_USERS,
                              TEXT(".Default\\Control Panel\\desktop\\ResourceLocale"),
                              0,
                              KEY_READ,
                              &hkey );

        if (dwErr == ERROR_SUCCESS) {

            dwSize = sizeof(buffer);
            dwErr = RegQueryValueExA( hkey,
                                     "",
                                     NULL,  //reserved
                                     NULL,  //type
                                     buffer,
                                     &dwSize );

            if(dwErr == ERROR_SUCCESS) {
                langid = LANGIDFROMLCID(strtoul(buffer,NULL,16));
            }
            RegCloseKey(hkey);
        }

        if ( dwErr != ERROR_SUCCESS ) {
           // Check HKLM\System\CurrentControlSet\Control\Nls\Locale

           dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                TEXT("System\\CurrentControlSet\\Control\\Nls\\Locale"),
                                0,
                                KEY_READ,
                                &hkey );

           if (dwErr == ERROR_SUCCESS) {

              dwSize = sizeof(buffer);
              dwErr = RegQueryValueExA( hkey,
                                        "",
                                        NULL,  //reserved
                                        NULL,  //type
                                        buffer,
                                        &dwSize );

              if (dwErr == ERROR_SUCCESS) {
                  langid = LANGIDFROMLCID(strtoul(buffer,NULL,16));
              }
              RegCloseKey(hkey);
           }
        }
    }

    return (langid);
}


LANGID
GetSourceNativeLangID (
    VOID
    )

/*++

Routine Description:

    [DefaultValues]
    Locale = xxxx

    every localized build has it's own Locale in intl.inf,

    so we use this value to identify source languag

Arguments:

Return Value:

   Language ID of source

--*/
{

    // BUGBUG - implement this by reading our own version info.

    LPCTSTR Type = (LPCTSTR) RT_VERSION;
    LPCTSTR Name = (LPCTSTR) 1;

    LANGINFO LangInfo;

    ZeroMemory(&LangInfo,sizeof(LangInfo));

    EnumResourceLanguages (
            NULL,   // our own module
            Type,
            Name,
            EnumLangProc,
            (LONG_PTR) &LangInfo
            );

    if ((LangInfo.Count > 2) || (LangInfo.Count < 1) ) {
        //
        // put error log here
        //
        // so far, for NT 3.51, only JPN has two language resources
    }

    return LangInfo.LangID;
}

DWORD
GetOsMajorId (
    VOID
    )
{
    POSVERSIONMAJORID p = g_OsVersionMajorId;

    while (p->Name) {
        if ((p->Platform == g_VersionInfo.dwPlatformId) &&
            (p->Major == g_VersionInfo.dwMajorVersion) &&
            (p->Minor == g_VersionInfo.dwMinorVersion)
            ) {
            return p->MajorId;
        }
        p++;
    }
    return 0;
}

DWORD
GetOsMinorId (
    VOID
    )
{
    POSVERSIONMINORID p = g_OsVersionMinorId;

    while (p->Name) {
        if ((p->Platform == g_VersionInfo.dwPlatformId) &&
            (p->Major == g_VersionInfo.dwMajorVersion) &&
            (p->Minor == g_VersionInfo.dwMinorVersion) &&
            (p->Build == g_VersionInfo.dwBuildNumber) &&
            ((p->CSDVer == NULL) || _tcsicmp (p->CSDVer, g_VersionInfo.szCSDVersion))
            ) {
            return p->MinorId;
        }
        p++;
    }
    return 0;
}

BOOL
TrustedDefaultUserLocale (
    LANGID LangID
    )
{
    PTRUSTEDSOURCELOCALE p = g_TrustedSourceLocale;

    while (p->LangId) {
        if ((!(p->ExcludedOs & GetOsMinorId ())) &&
            ((p->MinorOs & GetOsMinorId ()) || (p->MajorOs & GetOsMajorId ()))
           ) {
           return TRUE;
        }
        p++;
    }
    return FALSE;
}

BOOL
CheckLanguageVersion (
    LANGID SourceLangID,
    LANGID TargetLangID
    )
/*++

Routine Description:

    Check if the language of source NT is same as target NT or ,at least,

    compatibile

Arguments:

    Inf    handle of intl.inf

Return Value:

   TRUE  They are same or compatibile
   FALSE They are different

--*/
{
    PALTSOURCELOCALE p = g_AltSourceLocale;
    TCHAR TargetLangIDStr[9];

    LANGID SrcLANGID;
    LANGID DstLANGID;
    LANGID AltSourceLangID;

    //
    // If either one is 0, allow the upgrade. This is Windows 2000 Beta3 behavior.
    //
    if (SourceLangID == 0 || TargetLangID == 0) {
        return TRUE;
    }

    if (SourceLangID == TargetLangID) {
        return TRUE;
    }

    //
    // if Src != Dst, then we need to look up inf file to see
    //
    // if we can open a backdoor for Target language
    //

    //
    // use TargetLangID as key to find alternative SourceLangID
    //

    while (p->LangId) {
        //
        // Check if we found alternative locale
        //
        AltSourceLangID = LANGIDFROMLCID(p->AltLangId);
        if ((TargetLangID == p->LangId) &&
            (SourceLangID == AltSourceLangID)
            ) {
            //
            // We are here if we found alternative source lang,
            //
            // now check the version criteria
            //
            if ((!(p->ExcludedOs & GetOsMinorId ())) &&
                ((p->MinorOs & GetOsMinorId ()) || (p->MajorOs & GetOsMajorId ()))
               ) {
               return TRUE;
            }
        }
        p++;
    }
    return FALSE;
}


BOOL
InitLanguageDetection (
    VOID
    )
/*++

Routine Description:

    Initialize language detection and put the result in 3 global variables

    SourceNativeLangID  - LANGID of Source (NT is going to be installed)

    TargetNativeLangID  - LANGID of Target (OS system which is running)

    g_IsLanguageMatched - If language is not matched, then blocks upgrade

Arguments:

    None

Return Value:

   TRUE  init correctly
   FALSE init failed

--*/
{
    //
    // Init Global Variables
    //

    SourceNativeLangID  = GetSourceNativeLangID();

    TargetNativeLangID  = GetTargetNativeLangID();

    g_IsLanguageMatched = CheckLanguageVersion(SourceNativeLangID,TargetNativeLangID);

    if (!g_IsLanguageMatched) {
        if (SourceNativeLangID == 0x00000409) {
            // This is a localized system running an English wizard.
            // We want to allow that.
            g_IsLanguageMatched = TRUE;
        }
    }

    return TRUE;
}