//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//

#include "windows.h"
#include "coredll.h"
#include "pehdr.h"
#include "loader.h"
#include "loader_core.h"


#define DBGRSRC     DBGSHELL

typedef struct VERHEAD {
    WORD wTotLen;
    WORD wValLen;
    WORD wType;         /* always 0 */
    WCHAR szKey[(sizeof("VS_VERSION_INFO")+3)&~03];
    VS_FIXEDFILEINFO vsf;
} VERHEAD ;

typedef struct resroot_t {
    DWORD flags;
    DWORD timestamp;
    DWORD version;
    WORD  numnameents;
    WORD  numidents;
} resroot_t;

typedef struct resent_t {
    DWORD id;
    DWORD rva;
} resent_t;

typedef struct VERBLOCK {
    WORD wTotLen;
    WORD wValLen;
    WORD wType;
    WCHAR szKey[1];
} VERBLOCK ;

#pragma pack(2)
typedef struct {
    WORD idReserved;
    WORD idType;
    WORD idCount;
} ICONHEADER;

typedef struct ResourceDirectory {
    BYTE bWidth;
    BYTE bHeight;
    BYTE bColorCount;
    BYTE bReserved;
    WORD wPlanes;
    WORD wBitCount;
    DWORD dwBytesInRes;
    WORD wOrdinal;     /* points to an RT_ICON resource */
} RESDIR;

typedef struct {
    ICONHEADER ih;
    RESDIR rgdir[1];  /* may really be > 1 */
} HEADER_AND_DIR, *PHEADER_AND_DIR;

#define DWORDUP(x) (((x)+3)&~03)

#ifdef KCOREDLL

#define NKGetCallerResInfo  WIN32_CALL(PCInfo, GetCallerResInfo, (LPDWORD pdwBase, PUSERMODULELIST *ppModMui))

extern PUSERMODULELIST (* FindModule) (HMODULE hMod);

#define DoLoadLibrary(pszFileName, dwFlags)     ((PUSERMODULELIST) NKLoadLibraryEx (pszFileName, dwFlags, NULL))
#define DoFreeLibrary(pMod)                     NKFreeLibrary ((HMODULE) pMod)

// kernel DLL uses kernel's loader CS directly
#define LockLoader()
#define UnlockLoader()


#else  // KCOREDLL

extern PUSERMODULELIST g_pExeMUI;

extern DWORD g_dwExeBase;
extern struct info g_resExe;   // save the resource information of the exe

#define LockLoader()      LockProcCS()
#define UnlockLoader()    UnlockProcCS()

PUSERMODULELIST
DoLoadLibrary (
    LPCWSTR pszFileName,
    DWORD dwFlags
    );

BOOL 
DoFreeLibrary (
    PUSERMODULELIST pMod
    );

PUSERMODULELIST FindModule (HMODULE hMod);

PUSERMODULELIST FindModByResource (HRSRC hRsrc);


#endif // KCOREDLL

static BOOL IsValidProcess (HANDLE hProc)
{
    
#ifdef KCOREDLL
    return !hProc || ((DWORD) hProc == GetCallerVMProcessId ());
#else
    return !hProc || ((DWORD) hProc == GetCurrentProcessId ()) || (hProc == hActiveProc);
#endif
}

static PCInfo GetResourcInformation (HMODULE hMod, PUSERMODULELIST *ppModMui, LPDWORD pdwModBase)
{
    PCInfo pResInfo = NULL;

    if (IsValidProcess (hMod)) {
        // process

#ifdef KCOREDLL
        pResInfo    = NKGetCallerResInfo (pdwModBase, ppModMui);
#else
        *ppModMui   = g_pExeMUI;
        *pdwModBase = g_dwExeBase;
        pResInfo    = &g_resExe;
#endif
    } else {
        PUSERMODULELIST pMod;

        LockLoader ();
        pMod = FindModule (hMod);
        UnlockLoader ();
        
        if (pMod) {
            *ppModMui   = pMod->pmodResource;
            *pdwModBase = pMod->dwBaseAddr;
            pResInfo    = GetResInfo (pMod);
        }
    }
    return pResInfo;
}

//------------------------------------------------------------------------------
// Compare a Unicode string and a Pascal string
//------------------------------------------------------------------------------
static int StrCmpPascW (LPCWSTR ustr, LPCWSTR pstr) 
{
    int loop = (int)*pstr++;
    while (*ustr && loop--)
        if (towlower (*ustr++) != towlower (*pstr++))
            return 1;
    return (!*ustr && !loop ? 0 : 1);
}


//------------------------------------------------------------------------------
// Find first entry (from curr pointer)
//------------------------------------------------------------------------------
static DWORD FindFirst (DWORD curr) 
{
    resroot_t *rootptr = (resroot_t *)curr;
    resent_t *resptr = (resent_t *)(  curr
                                    + sizeof(resroot_t)
                                    + rootptr->numnameents*sizeof(resent_t));
    return resptr->rva;
}


//------------------------------------------------------------------------------
// Search for a named type
//------------------------------------------------------------------------------
static DWORD FindTypeByName (DWORD vbase, DWORD curr, LPCWSTR str) 
{
    resroot_t *rootptr = (resroot_t *)curr;
    resent_t *resptr = (resent_t *)(curr + sizeof(resroot_t));
    int count = rootptr->numnameents;
    while (count--) {
        if (!str || !StrCmpPascW (str, (LPCWSTR)(vbase+(resptr->id&0x7fffffff)))) {
            return resptr->rva;
        }
        resptr++;
    }
    return 0;
}


//------------------------------------------------------------------------------
// Search for a numbered type
//------------------------------------------------------------------------------
static DWORD FindTypeByNum (DWORD curr, DWORD id) 
{
    resroot_t *rootptr = (resroot_t *)curr;
    resent_t *resptr = (resent_t *)(curr + sizeof(resroot_t) +
        rootptr->numnameents*sizeof(resent_t));
    int count = rootptr->numidents;
    while (count--) {
        if (!id || (resptr->id == id)) {
            return resptr->rva;
        }
        resptr++;
    }
    return 0;
}


//------------------------------------------------------------------------------
// Find type entry (by name or number)
//------------------------------------------------------------------------------
static DWORD FindType (DWORD vbase, DWORD curr, LPCWSTR str) 
{
    DWORD res;
    if (((DWORD)str) >> 16) {
        res = (str[0] == (WCHAR)'#')
            ? FindTypeByNum  (curr, _wtol (str+1))
            : FindTypeByName (vbase, curr, str);
        
    } else if (!(res = FindTypeByNum (curr, (DWORD)str)) && !str) {
        res = FindTypeByName (vbase, curr, str);
    }
    return res;
}

//------------------------------------------------------------------------------
// DoFindResource - worker function to find a resource
//------------------------------------------------------------------------------
static HRSRC DoFindResource (
        PCInfo      pResInfo,
        LPCWSTR     lpszName,
        LPCWSTR     lpszType
        ) 
{
    HRSRC hrsrc = NULL;
    DWORD vbase;
    DEBUGMSG (DBGRSRC && pResInfo, (L"DoFindResource: resource base = %8.8lx\n", pResInfo->rva));


    if (pResInfo && (vbase = pResInfo->rva)) {
        DWORD trva;

        if (   (trva = FindType  (vbase, vbase, (LPCWSTR)lpszType))
            && (trva = FindType  (vbase, vbase + (trva & 0x7fffffff), (LPCWSTR)lpszName))
            && (trva = FindFirst (vbase + (trva & 0x7fffffff)))) {
            hrsrc = (HRSRC) (vbase + (trva & 0x7fffffff));
        }
    }
    DEBUGMSG (DBGRSRC, (L"DoFindResource: return %8.8lx\n", hrsrc));
    return hrsrc;
}


extern "C" HRSRC WINAPI FindResourceW (HMODULE hModule, LPCWSTR lpszName, LPCWSTR lpszType)
{
    PUSERMODULELIST pModMui;
    DWORD    dwModBase;
    HRSRC    hRsrc = NULL;
    PCInfo   pResInfo = GetResourcInformation (hModule, &pModMui, &dwModBase);

    __try {
        if (pResInfo 
            && !(hRsrc = DoFindResource (GetResInfo (pModMui), lpszName, lpszType))) {
            hRsrc = DoFindResource (pResInfo, lpszName, lpszType);
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        // empty
    }

    if (!hRsrc) {
        SetLastError (pResInfo? ERROR_RESOURCE_NAME_NOT_FOUND : ERROR_INVALID_HANDLE);
    }

    return hRsrc;
}

extern "C" HGLOBAL WINAPI LoadResource (HMODULE hModule, HRSRC hrsrc)
{
    HGLOBAL hRet = NULL;

    if (hrsrc) {
        PUSERMODULELIST pModMui;
        DWORD    dwModBase;
        PCInfo   pResInfo = GetResourcInformation (hModule, &pModMui, &dwModBase);

        if (pResInfo) {
            __try {
                // try base module
                if (IsResValid (pResInfo, hrsrc)) {
                    hRet = (HGLOBAL) (dwModBase + ((resdata_t *) hrsrc)->rva);

                // try MUI if not in base module
                } else if (IsResValid (GetResInfo (pModMui), hrsrc)) {
                    hRet = (HGLOBAL) (pModMui->dwBaseAddr + ((resdata_t *) hrsrc)->rva);
                }
#ifndef KCOREDLL
                // user mode - hModule is NULL, try find it in the module list.
                else if (!hModule && (pModMui = FindModByResource (hrsrc))) {
                    hRet = (HGLOBAL) (pModMui->dwBaseAddr + ((resdata_t *) hrsrc)->rva);
                }
#endif
            } __except (EXCEPTION_EXECUTE_HANDLER) {
                // empty
            }
        }
    }

    if (!hRet) {
        SetLastError (ERROR_INVALID_HANDLE);
    }

    return hRet;
}

extern "C" int WINAPI LoadStringW (HINSTANCE hInstance, UINT wID, LPWSTR lpBuffer, int nBufMax)
{
    PUSERMODULELIST pModMui;
    PCInfo          pResInfo;
    DWORD           dwModBase;
    HRSRC           hRsrc;
    int             Ret = 0;
    DWORD           dwErr = 0;

    DEBUGMSG (DBGRSRC, (L"LoadStringW: 0x%8.8lx 0x%x 0x%8.8lx %d\n", hInstance, wID, lpBuffer, nBufMax));

    __try {
        // Make sure the parms are valid.
        if ((lpBuffer && (nBufMax <= 1))
            || !(pResInfo = GetResourcInformation (hInstance, &pModMui, &dwModBase))) {
            dwErr = ERROR_INVALID_PARAMETER;

        } else if (hRsrc = DoFindResource (GetResInfo (pModMui), MAKEINTRESOURCE((wID >> 4) + 1), RT_STRING)) {
            // resource found in MUI
            pResInfo  = GetResInfo (pModMui);
            dwModBase = pModMui->dwBaseAddr;
            
        } else if (!(hRsrc = DoFindResource (pResInfo, MAKEINTRESOURCE((wID >> 4) + 1), RT_STRING))) {
            // resource not found
            dwErr = ERROR_RESOURCE_NAME_NOT_FOUND;
        }

        if (!dwErr) {

            LPWSTR lpsz;
            int    cch = 0;

            nBufMax --;     // room for EOS

            // String Tables are broken up into 16 string segments.  Find the segment
            // containing the string we are interested in.

            // map the resouce to the secure section if it's a DLL
            // since it's always going to be there
            lpsz = (LPWSTR) (dwModBase + ((resdata_t *) hRsrc)->rva);

            // Move past the other strings in this segment.
            wID &= 0x0F;
            while (TRUE) {
                cch = *lpsz++;
                if (wID-- == 0)
                    break;
                lpsz += cch;
            }

            if (lpBuffer) {
                // Don't copy more than the max allowed.
                if (cch > nBufMax)
                    cch = nBufMax;
                // Copy the string into the buffer
                memcpy (lpBuffer, lpsz, cch * sizeof(TCHAR));
                
            } else if (cch) { // don't return zero-length strings
                // The return value is the string itself

                // return a zero based address.
                Ret = (int) lpsz;
            }

            if (lpBuffer) { // if we are not returning the string as the return value
                // Append a NULL
                lpBuffer[Ret = cch] = 0;
            }
        }

    } __except (EXCEPTION_EXECUTE_HANDLER) {
        dwErr = ERROR_INVALID_PARAMETER;
    }

    if (dwErr) {
        SetLastError (dwErr);
        Ret = 0;
    }
    DEBUGMSG (DBGRSRC, (L"LoadStringW Exits: %d\n", Ret));
    return Ret;
}

extern "C" DWORD WINAPI SizeofResource (HMODULE hModule, HRSRC hRsrc)
{
    DWORD dwRet = 0;
    __try {
        dwRet = ((resdata_t *)hRsrc)->size;
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        SetLastError (ERROR_INVALID_HANDLE);
    }
    return dwRet;
}

static LPBYTE DoExtractResrouce (PUSERMODULELIST pMod, LPCWSTR lpszName, LPCWSTR lpszType, LPDWORD pdwSize)
{
    HRSRC   hrsrc;
    LPBYTE  pRet    = NULL;
    
    __try {
        hrsrc = DoFindResource (GetResInfo (pMod->pmodResource), lpszName, lpszType);  // try MUI first
        DWORD   cbRsrc;

        if (hrsrc) {
            // in MUI
            DEBUGCHK (pMod->pmodResource);
            pMod = pMod->pmodResource;

        } else if (!(hrsrc = DoFindResource (GetResInfo (pMod), lpszName, lpszType))) {
            // not in base either
            SetLastError (ERROR_RESOURCE_NAME_NOT_FOUND);
        }
        
        if (hrsrc && ((int) (cbRsrc = ((resdata_t *)hrsrc)->size) > 0)) {
            
            if (!(pRet = (LPBYTE) LocalAlloc (LMEM_FIXED, cbRsrc))) {
                SetLastError (ERROR_NOT_ENOUGH_MEMORY);
            } else {
                memcpy (pRet, (LPVOID) (pMod->dwBaseAddr + ((resdata_t *) hrsrc)->rva), cbRsrc);
                if (pdwSize) {
                    *pdwSize = cbRsrc;
                }
            }
        } else {
            SetLastError (ERROR_RESOURCE_NAME_NOT_FOUND);
        }
        
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        if (pRet) {
            LocalFree (pRet);
            pRet = NULL;
        }
        SetLastError (ERROR_INVALID_PARAMETER);
    }
    return pRet;
}

static LPVOID ExtractResourceWithSize (LPCWSTR lpszFile, LPCWSTR lpszName, LPCWSTR lpszType, LPDWORD pdwSize)
{
    PUSERMODULELIST pMod;
    LPVOID  prsrc = NULL;

    LockLoader ();
    pMod = DoLoadLibrary (lpszFile, LOAD_LIBRARY_AS_DATAFILE);
    UnlockLoader ();

    if (pMod) {
        prsrc = DoExtractResrouce (pMod, lpszName, lpszType, pdwSize);

        LockLoader ();
        DoFreeLibrary (pMod);
        UnlockLoader ();
    }
    if (!prsrc) {
        SetLastError (ERROR_RESOURCE_NAME_NOT_FOUND);
    }
    return prsrc;
}

extern "C" LPVOID ExtractResource (LPCWSTR lpszFile, LPCWSTR lpszName, LPCWSTR lpszType)
{
    return ExtractResourceWithSize (lpszFile, lpszName, lpszType, NULL);
}


extern "C" BOOL WINAPI VerQueryValueW (const LPVOID pvblk, LPWSTR lpSubBlock, LPVOID * lplpBuffer, PUINT puLen)
{
    VERBLOCK *pBlock = (VERBLOCK *) pvblk;
    LPWSTR lpSubBlockOrg, lpEndBlock, lpEndSubBlock, lpStart;
    WCHAR cEndBlock, cTemp;
    BOOL bString;
    DWORD dwTotBlockLen, dwHeadLen;
    int nCmp;

    *puLen = 0;
    if ((int)pBlock->wTotLen < sizeof(VERBLOCK)) {
        SetLastError (ERROR_INVALID_DATA);
        return FALSE;
    }
    if (!(lpSubBlockOrg = (LPWSTR)LocalAlloc(LMEM_FIXED,(wcslen(lpSubBlock)+1)*sizeof(WCHAR)))) {
        SetLastError (ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }
    wcscpy(lpSubBlockOrg,lpSubBlock);
    lpSubBlock = lpSubBlockOrg;
    lpEndBlock = (LPWSTR)((LPBYTE)pBlock + pBlock->wTotLen - sizeof(WCHAR));
    cEndBlock = *lpEndBlock;
    *lpEndBlock = 0;
    bString = FALSE;
    while (*lpSubBlock) {
        while (*lpSubBlock == L'\\')
            ++lpSubBlock;
        if (*lpSubBlock) {
            dwTotBlockLen = (DWORD)((LPSTR)lpEndBlock - (LPSTR)pBlock + sizeof(WCHAR));
            if (((int)dwTotBlockLen < sizeof(VERBLOCK)) || (pBlock->wTotLen > (WORD)dwTotBlockLen))
                goto NotFound;
            dwHeadLen = DWORDUP(sizeof(VERBLOCK) - sizeof(WCHAR) + (wcslen(pBlock->szKey) + 1) * sizeof(WCHAR)) + DWORDUP(pBlock->wValLen);
            if (dwHeadLen > pBlock->wTotLen)
                goto NotFound;
            lpEndSubBlock = (LPWSTR)((LPBYTE)pBlock + pBlock->wTotLen);
            pBlock = (VERBLOCK*)((LPBYTE)pBlock+dwHeadLen);
            for (lpStart=lpSubBlock; *lpSubBlock && (*lpSubBlock!=L'\\'); lpSubBlock++)
                ;
            cTemp = *lpSubBlock;
            *lpSubBlock = 0;
            nCmp = 1;
            while (((int)pBlock->wTotLen > sizeof(VERBLOCK)) && ((int)pBlock->wTotLen <= (LPBYTE)lpEndSubBlock-(LPBYTE)pBlock)) {
                if (!(nCmp=wcsicmp(lpStart, pBlock->szKey)))
                    break;
                pBlock=(VERBLOCK*)((LPBYTE)pBlock+DWORDUP(pBlock->wTotLen));
            }
            *lpSubBlock = cTemp;
            if (nCmp)
                goto NotFound;
        }
    }
    *puLen = pBlock->wValLen;
    lpStart = (LPWSTR)((LPBYTE)pBlock+DWORDUP((sizeof(VERBLOCK)-sizeof(WCHAR))+(wcslen(pBlock->szKey)+1)*sizeof(WCHAR)));
    *lplpBuffer = lpStart < (LPWSTR)((LPBYTE)pBlock+pBlock->wTotLen) ? lpStart : (LPWSTR)(pBlock->szKey+wcslen(pBlock->szKey));
    bString = pBlock->wType;
    *lpEndBlock = cEndBlock;
    LocalFree(lpSubBlockOrg);
    return (TRUE);
NotFound:
    SetLastError (ERROR_RESOURCE_NAME_NOT_FOUND);
    *lpEndBlock = cEndBlock;
    LocalFree(lpSubBlockOrg);
    return (FALSE);
}

extern "C" DWORD WINAPI GetFileVersionInfoSizeW (LPWSTR lpFilename, LPDWORD lpdwHandle)
{
    DWORD   dwRet = 0;
    VERHEAD *pVerHead;
    
    DEBUGMSG(DBGRSRC, (L"GetFileVersionInfoSizeW entry: %8.8lx %8.8lx\r\n",lpFilename, lpdwHandle));

    if (lpdwHandle) {
        *lpdwHandle = 0;
    }

    if (pVerHead = (VERHEAD*) ExtractResourceWithSize (lpFilename, MAKEINTRESOURCE(VS_VERSION_INFO), VS_FILE_INFO, &dwRet)) {
        if (((DWORD)pVerHead->wTotLen > dwRet) || (pVerHead->vsf.dwSignature != VS_FFI_SIGNATURE))
            SetLastError (ERROR_INVALID_DATA);
        else
            dwRet = pVerHead->wTotLen;
        LocalFree (pVerHead);
    }

    DEBUGMSG(DBGRSRC, (L"GetFileVersionInfoSizeW exit: %8.8lx\r\n",dwRet));
    return dwRet;
}

extern "C" BOOL WINAPI GetFileVersionInfoW (LPWSTR lpFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    BOOL bRet = FALSE;
    DWORD dwLength = 0;
    VERHEAD *pVerHead;

    DEBUGMSG(DBGRSRC,(L"GetFileVersionInfoW entry: %8.8lx %8.8lx %8.8lx %8.8lx\r\n",lpFilename, dwHandle, dwLen, lpData));
    if (pVerHead = (VERHEAD *)ExtractResourceWithSize (lpFilename, MAKEINTRESOURCE(VS_VERSION_INFO), VS_FILE_INFO, &dwLength)) {
        
        if (((DWORD)pVerHead->wTotLen > dwLength) || (pVerHead->vsf.dwSignature != VS_FFI_SIGNATURE)) {
            SetLastError (ERROR_INVALID_DATA);
            
        } else {

            if (dwLen > pVerHead->wTotLen) {
                dwLen = pVerHead->wTotLen;
            }
            memcpy (lpData, pVerHead, dwLen);
            ((VERHEAD*)lpData)->wTotLen = (WORD) dwLen;
            bRet = TRUE;
        
        }
        LocalFree(pVerHead);
    } else {
        SetLastError (ERROR_INVALID_DATA);
    }
    DEBUGMSG(DBGRSRC,(L"GetFileVersionInfoW exit: %d\r\n",bRet));
    return bRet;;
}

typedef void (* KEICB_t)(LPVOID, PHEADER_AND_DIR,PWORD);

extern "C" UINT KernExtractIcons (LPCWSTR lpszFile, int nIconIndex, LPBYTE * pIconLarge, LPBYTE * pIconSmall, CALLBACKINFO * pcbi)
{
    PUSERMODULELIST pMod;
    PUSERMODULELIST pModRes;
    UINT    nRet = 0;

    LockLoader ();
    pMod = DoLoadLibrary (lpszFile, LOAD_LIBRARY_AS_DATAFILE);
    UnlockLoader ();

    if (pModRes = pMod) {
        PHEADER_AND_DIR phd;
        HRSRC hrsrc = DoFindResource (GetResInfo (pMod->pmodResource), MAKEINTRESOURCE(nIconIndex), RT_GROUP_ICON);

        if (hrsrc) {
            DEBUGCHK (pMod->pmodResource);
            pModRes = pMod->pmodResource;
        } else {
            hrsrc = DoFindResource (GetResInfo (pMod), MAKEINTRESOURCE(nIconIndex), RT_GROUP_ICON);
        }

        if (hrsrc && (phd = (PHEADER_AND_DIR) (pModRes->dwBaseAddr + ((resdata_t *) hrsrc)->rva))) {
            if (phd->ih.idCount) {
                WORD  index[2] = {0, 1};
                ((KEICB_t)pcbi->pfn)(pcbi->pvArg0, phd, index);
                if (pIconLarge && (*pIconLarge = DoExtractResrouce (pModRes, MAKEINTRESOURCE(phd->rgdir[index[0]].wOrdinal), RT_ICON, NULL)))
                    nRet++;
                if (pIconSmall && (*pIconSmall = DoExtractResrouce (pModRes, MAKEINTRESOURCE(phd->rgdir[index[1]].wOrdinal), RT_ICON, NULL)))
                    nRet++;
            }
        }
        LockLoader ();
        DoFreeLibrary (pMod);
        UnlockLoader ();
    }
    if (!nRet) {
        SetLastError (ERROR_RESOURCE_NAME_NOT_FOUND);
    }
    return nRet;
}


