//+---------------------------------------------------------------------------
//
//  File:       win32.cpp
//
//  Contents:   Implementation for the Windows 32 Read/Write module
//
//  Classes:    one
//
//  History:    05-Jul-93   alessanm    created
//
//----------------------------------------------------------------------------

#include <afxwin.h>
#include "..\common\rwdll.h"
#include "..\common\rw32hlpr.h"

#include <limits.h>
#include <malloc.h>

/////////////////////////////////////////////////////////////////////////////
// Initialization of MFC Extension DLL

#include "afxdllx.h"    // standard MFC Extension DLL routines

static AFX_EXTENSION_MODULE NEAR extensionDLL = { NULL, NULL };

/////////////////////////////////////////////////////////////////////////////
// Check sum function

DWORD FixCheckSum( LPCSTR ImageName, LPCSTR OrigFileName, LPCSTR SymbolPath );


/////////////////////////////////////////////////////////////////////////////
// General Declarations
#define RWTAG "WIN32"

static RESSECTDATA ResSectData;
static ULONG gType;
static ULONG gLng;
static ULONG gResId;
static WCHAR gwszResId[256];
static WCHAR gwszTypeId[256];

/////////////////////////////////////////////////////////////////////////////
// Function Declarations
static LONG WriteResInfo(
                 LPLPBYTE lpBuf, LONG* uiBufSize,
                 WORD wTypeId, LPSTR lpszTypeId, BYTE bMaxTypeLen,
                 WORD wNameId, LPSTR lpszNameId, BYTE bMaxNameLen,
                 DWORD dwLang,
                 DWORD dwSize, DWORD dwFileOffset );

static UINT GetUpdatedRes(
                 BYTE far * far* lplpBuffer,
                 UINT* uiSize,
                 WORD* wTypeId, LPSTR lplpszTypeId,
                 WORD* wNameId, LPSTR lplpszNameId,
                 DWORD* dwlang, DWORD* dwSize );

static UINT GetRes(
                 BYTE far * far* lplpBuffer,
                 UINT* puiBufSize,
                 WORD* wTypeId, LPSTR lplpszTypeId,
                 WORD* wNameId, LPSTR lplpszNameId,
                 DWORD* dwLang, DWORD* dwSize, DWORD* dwFileOffset );


static UINT FindResourceSection( CFile*, ULONG_PTR * );

static LONG ReadFile(CFile*, UCHAR *, LONG);
static UINT ParseDirectory( CFile*,
                            LPLPBYTE lpBuf, UINT* uiBufSize,
                            BYTE,
                            PIMAGE_RESOURCE_DIRECTORY,
                            PIMAGE_RESOURCE_DIRECTORY );

static UINT ParseDirectoryEntry( CFile*,
                                 LPLPBYTE lpBuf, UINT* uiBufSize,
                                 BYTE,
                                 PIMAGE_RESOURCE_DIRECTORY,
                                 PIMAGE_RESOURCE_DIRECTORY_ENTRY );

static UINT ParseSubDir( CFile*,
                         LPLPBYTE lpBuf, UINT* uiBufSize,
                         BYTE,
                         PIMAGE_RESOURCE_DIRECTORY,
                         PIMAGE_RESOURCE_DIRECTORY_ENTRY );

static UINT ProcessData( CFile*,
                         LPLPBYTE lpBuf, UINT* uiBufSize,
                         PIMAGE_RESOURCE_DIRECTORY,
                         PIMAGE_RESOURCE_DATA_ENTRY );


/////////////////////////////////////////////////////////////////////////////
// Public C interface implementation

//[registration]
extern "C"
BOOL    FAR PASCAL RWGetTypeString(LPSTR lpszTypeName)
{
    strcpy( lpszTypeName, RWTAG );
    return FALSE;
}

extern "C"
BOOL    FAR PASCAL RWValidateFileType   (LPCSTR lpszFilename)
{
    TRACE("WIN32.DLL: RWValidateFileType()\n");

    CFile file;

    // we Open the file to see if it is a file we can handle
    if (!file.Open( lpszFilename, CFile::typeBinary | CFile::modeRead | CFile::shareDenyNone))
        return FALSE;

    // Read the file signature
    WORD w;
    file.Read((WORD*)&w, sizeof(WORD));
    if (w==IMAGE_DOS_SIGNATURE) {
    file.Seek( 0x18, CFile::begin );
    file.Read((WORD*)&w, sizeof(WORD));
    if (w<0x0040) {
        // this is not a Windows Executable
            file.Close();
        return FALSE;
    }
    // get offset to header
    file.Seek( 0x3c, CFile::begin );
    file.Read((WORD*)&w, sizeof(WORD));
    // get windows magic word
        file.Seek( w, CFile::begin );
    file.Read((WORD*)&w, sizeof(WORD));
    if (w==LOWORD(IMAGE_NT_SIGNATURE)) {
        file.Read((WORD*)&w, sizeof(WORD));
        if (w==HIWORD(IMAGE_NT_SIGNATURE)) {
            // this is a Windows NT Executable
        // we can handle the situation
        file.Close();
        return TRUE;
            }
    }
    }
    file.Close();
    return FALSE;
}

extern "C"
DllExport
UINT
APIENTRY
RWReadTypeInfo(
    LPCSTR lpszFilename,
    LPVOID lpBuffer,
    UINT* puiSize

    )
{
    TRACE("WIN32.DLL: RWReadTypeInfo()\n");
    UINT uiError = ERROR_NO_ERROR;
    BYTE far * lpBuf = (BYTE far *)lpBuffer;
    UINT uiBufSize = *puiSize;
    CFile file;
    // check if it is  a valid win32 file
    if (!RWValidateFileType(lpszFilename))
        return ERROR_RW_INVALID_FILE;

    // Make sure we are using the right code page and global settings
    // Get the pointer to the function
	HINSTANCE hDllInst = LoadLibrary("iodll.dll");
    if (hDllInst)
    {
        UINT (FAR PASCAL * lpfnGetSettings)(LPSETTINGS);
        // Get the pointer to the function to get the settings
        lpfnGetSettings = (UINT (FAR PASCAL *)(LPSETTINGS))
                            GetProcAddress( hDllInst, "RSGetGlobals" );
        if (lpfnGetSettings!=NULL) {
            SETTINGS settings;
	        (*lpfnGetSettings)(&settings);

    	    g_cp      = settings.cp;
            g_bAppend = settings.bAppend;
            g_bUpdOtherResLang = settings.bUpdOtherResLang;
            strcpy( g_char, settings.szDefChar );
		}

        FreeLibrary(hDllInst);
    }


    // Parse the resource tree and extract the information
    // Open the file and try to read the information on the resource in it.
    if (!file.Open(lpszFilename, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone))
        return ERROR_FILE_OPEN;

    // we try to read as much information as we can
    // Because this is a res file we can read all the information we need.

    UINT uiBufStartSize = uiBufSize;


    UCHAR * pResources = LPNULL;
    uiError = FindResourceSection( &file, (ULONG_PTR *)&pResources );
    if (uiError) {
        file.Close();
        return uiError;
    }
    uiError = ParseDirectory( &file,
                              (LPLPBYTE) &lpBuffer, &uiBufSize,
                              0,
                              (PIMAGE_RESOURCE_DIRECTORY)pResources,
                              (PIMAGE_RESOURCE_DIRECTORY)pResources );

    free(pResources);

    file.Close();
    *puiSize = uiBufStartSize-uiBufSize;
    return uiError;
}

extern "C"
DllExport
DWORD
APIENTRY
RWGetImage(
    LPCSTR  lpszFilename,
    DWORD   dwImageOffset,
    LPVOID  lpBuffer,
    DWORD   dwSize
    )
{
    UINT uiError = ERROR_NO_ERROR;
    BYTE far * lpBuf = (BYTE far *)lpBuffer;
    DWORD dwBufSize = dwSize;
    // we can consider the use of a CMemFile so we get the same speed as memory access.
    CFile file;

    // Open the file and try to read the information on the resource in it.
    if (!file.Open(lpszFilename, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone))
        return (DWORD)ERROR_FILE_OPEN;

    if ( dwImageOffset!=(DWORD)file.Seek( dwImageOffset, CFile::begin) )
        return (DWORD)ERROR_FILE_INVALID_OFFSET;
    if (dwSize>UINT_MAX) {
        // we have to read the image in different steps
        return (DWORD)0L;
    } else uiError = file.Read( lpBuf, (UINT)dwSize);
    file.Close();

    return (DWORD)uiError;
}

extern "C"
DllExport
UINT
APIENTRY
RWParseImageEx(
	LPCSTR  lpszType,
    LPCSTR  lpszResId,
	LPVOID  lpImageBuf,
	DWORD   dwImageSize,
	LPVOID  lpBuffer,
	DWORD   dwSize,
    LPCSTR  lpRCFilename
	)
{
    UINT uiError = ERROR_NO_ERROR;
    BYTE far * lpBuf = (BYTE far *)lpBuffer;
    DWORD dwBufSize = dwSize;

    // The Type we can parse are only the standard ones
    // This function should fill the lpBuffer with an array of ResItem structure
    if (HIWORD(lpszType))
    {
        if (strcmp(lpszType, "REGINST") ==0)
        {
            return (ParseEmbeddedFile( lpImageBuf, dwImageSize,  lpBuffer, dwSize ));
        }
    }
    switch ((UINT)LOWORD(lpszType)) {
        case 1:
        case 12:
        	uiError = ParseEmbeddedFile( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;
        case 2:
        case 14:
        	uiError = ParseEmbeddedFile( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;

        case 3:
        	uiError = ParseEmbeddedFile( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;

        case 4:
            uiError = ParseMenu( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;

        case 5:
            uiError = ParseDialog( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;
        case 6:
            uiError = ParseString( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;
        case 9:
            uiError = ParseAccel( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;
        case 11:
            uiError = ParseMsgTbl( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;
        case 16:
            uiError = ParseVerst( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;

        case 23:
        case 240:
        case 2110:
        case 1024:
            uiError = ParseEmbeddedFile( lpImageBuf, dwImageSize,  lpBuffer, dwSize );
        break;

        case 7:
        case 8:
        case 13:
        case 15:
        break;
        //
        // To support RCDATA and user defined function we will call back the iodll,
        // get the file name and check if we have a DLL that will handle RCDATA.
        // We expect the DLL name to be RCfilename.dll.
        // This Dll will export a function called RWParseImageEx. This function will
        // be called by the RW to fill the buffer, all this without the iodll knowing.
        //
        case 10:
        default:
            //
            // Get the file name from the iodll
            //
            if(lpRCFilename && strcmp(lpRCFilename, ""))
            {
                // try to Load the dll
                HINSTANCE hRCDllInst = LoadLibrary(lpRCFilename);
                if (hRCDllInst)
                {
                    UINT (FAR PASCAL  * lpfnParseImageEx)(LPCSTR, LPCSTR, LPVOID, DWORD, LPVOID, DWORD, LPCSTR);

                    // Get the pointer to the function to extract the resources
                    lpfnParseImageEx = (UINT (FAR PASCAL *)(LPCSTR, LPCSTR, LPVOID, DWORD, LPVOID, DWORD, LPCSTR))
                                        GetProcAddress( hRCDllInst, "RWParseImageEx" );

                    if (lpfnParseImageEx)
                    {
                        uiError = (*lpfnParseImageEx)(lpszType,
                                     lpszResId,
                                     lpImageBuf,
                                     dwImageSize,
                                     lpBuffer,
                                     dwSize,
                                     NULL);
                    }

                    FreeLibrary(hRCDllInst);
                }
            }

        break;
    }

    return uiError;
}

extern "C"
DllExport
UINT
APIENTRY
RWParseImage(
    LPCSTR  lpszType,
    LPVOID  lpImageBuf,
    DWORD   dwImageSize,
    LPVOID  lpBuffer,
    DWORD   dwSize
    )
{
    //
    // Just a wrapper to be compatible...
    //
    return RWParseImageEx(lpszType, NULL, lpImageBuf, dwImageSize, lpBuffer, dwSize, NULL);
}

extern"C"
DllExport
UINT
APIENTRY
RWWriteFile(
    LPCSTR          lpszSrcFilename,
    LPCSTR          lpszTgtFilename,
    HANDLE          hResFileModule,
    LPVOID          lpBuffer,
    UINT            uiSize,
    HINSTANCE       hDllInst,
    LPCSTR          lpszSymbolPath
    )
{
    UINT uiError = ERROR_NO_ERROR;
    UINT uiBufSize = uiSize;
    CFile fileIn;
    CFile fileOut;
    BOOL  bfileIn = TRUE;


    // Open the file and try to read the information on the resource in it.
	CFileStatus status;
    if (CFile::GetStatus( lpszSrcFilename, status )) {
        // check if the size of the file is not null
        if (!status.m_size)
            CFile::Remove(lpszSrcFilename);
    }

    if (!fileIn.Open(lpszSrcFilename, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone))
        return ERROR_FILE_OPEN;

    if (!fileOut.Open(lpszTgtFilename, CFile::modeWrite | CFile::modeCreate | CFile::typeBinary))
        return ERROR_FILE_CREATE;

    // Create a copy of the US file
    uiError = CopyFile( &fileIn, &fileOut );

    fileIn.Close();
    fileOut.Close();

    // Get the pointer to the function
	hDllInst = LoadLibrary("iodll.dll");
    if (!hDllInst)
        return ERROR_DLL_LOAD;

    DWORD (FAR PASCAL * lpfnGetImage)(HANDLE, LPCSTR, LPCSTR, DWORD, LPVOID, DWORD);
    // Get the pointer to the function to extract the resources image
    lpfnGetImage = (DWORD (FAR PASCAL *)(HANDLE, LPCSTR, LPCSTR, DWORD, LPVOID, DWORD))
                        GetProcAddress( hDllInst, "RSGetResImage" );
    if (lpfnGetImage==NULL) {
        FreeLibrary(hDllInst);
        return (UINT)GetLastError()+LAST_ERROR;
    }

    // We read the resources from the file and then we check if the resource has been updated
    // or if we can just copy it

    WORD wTypeId;
    char szTypeId[128];

    WORD wNameId;
    char szNameId[128];

    DWORD dwSize;
    DWORD dwLang;

    WORD wUpdTypeId = 0;
    static char szUpdTypeId[128];

    WORD wUpdNameId;
    static char szUpdNameId[128];

    static WCHAR szwTypeId[128];
    static WCHAR szwNameId[128];

    DWORD dwUpdLang = 0;
    DWORD dwUpdSize = 0;

    UINT uiBufStartSize = uiBufSize;
    DWORD dwImageBufSize;
    DWORD dwLstErr = 0l;
    BYTE * lpImageBuf;
    static WCHAR szwTgtFilename[400];

    SetLastError(0);
    // Convert the Target file name to a unicode name
    _MBSTOWCS(szwTgtFilename, (char *)lpszTgtFilename, 400 );

    // Get the updated resource and replace them
    HANDLE hUpd = BeginUpdateResourceW( (LPCWSTR)&szwTgtFilename[0], !g_bAppend );
    dwLstErr = GetLastError();

    if (!hUpd) {
        FreeLibrary(hDllInst);
        return((UINT)dwLstErr);
    }

    // Parse the original file an get the list of resources

    UINT uiBSize = 100000;
    BYTE far * lpBuf = new far BYTE[uiBSize];
    BYTE far * lpStartBuf = lpBuf;
    if (!lpBuf) {
        FreeLibrary(hDllInst);
        return ERROR_NEW_FAILED;
    }

    uiError = RWReadTypeInfo( lpszSrcFilename, (LPVOID)lpBuf, &uiBSize );
    if (uiError!=ERROR_NO_ERROR) {
        FreeLibrary(hDllInst);
        delete lpBuf;
        return uiError;
    }

    DWORD dwDummy;

    while(uiBSize>0) {
        if (uiBSize)
            GetRes( &lpBuf,
                    &uiBSize,
                    &wTypeId, &szTypeId[0],
                    &wNameId, &szNameId[0],
                    &dwLang,
                    &dwSize,
                    &dwDummy
            );

        dwLang = MAKELONG(LOWORD(dwLang),LOWORD(dwLang));

        if ((!wUpdTypeId) && (uiBufSize))
            GetUpdatedRes( (BYTE**)&lpBuffer,
                    &uiBufSize,
                    &wUpdTypeId, &szUpdTypeId[0],
                    &wUpdNameId, &szUpdNameId[0],
                    &dwUpdLang,
                    &dwUpdSize
                    );

        // check if the resource has been updated or not
        if ( (wUpdTypeId==wTypeId) &&
             ( (CString)szUpdTypeId==(CString)szTypeId) &&
             (wUpdNameId==wNameId) &&
             ( (CString)szUpdNameId==(CString)szNameId) &&
             (LOWORD(dwLang) == LOWORD(dwUpdLang))
           ) {
             dwLang = dwUpdLang;
             dwSize = dwUpdSize;
             wUpdTypeId = 0;
        }


        // all resources of specific language need to be marked
        if (LOWORD(dwLang) == LOWORD(dwUpdLang) && g_bUpdOtherResLang)
        {
            dwLang = dwUpdLang;
        }


        // The resource has been updated get the image from the IODLL
        lpImageBuf = new BYTE[dwSize];

        // convert the Name to unicode
        LPWSTR  lpUpdType = LPNULL;
        LPWSTR  lpUpdRes = LPNULL;
        LPCSTR  lpType = LPNULL;
        LPCSTR  lpRes = LPNULL;

        if (wTypeId) {
            lpUpdType = (LPWSTR) MAKEINTRESOURCE((WORD)wTypeId);
            lpType = MAKEINTRESOURCE((WORD)wTypeId);
        } else {
            SetLastError(0);
            _MBSTOWCS(szwTypeId, szTypeId, 128 );
            // Check for error
            if(GetLastError()) {
                FreeLibrary(hDllInst);
                return ERROR_DLL_LOAD;
            }
            lpUpdType = (LPWSTR) &szwTypeId[0];
            lpType = &szTypeId[0];
        }

        if (wNameId) {
            lpUpdRes = (LPWSTR) MAKEINTRESOURCE((WORD)wNameId);
            lpRes = MAKEINTRESOURCE((WORD)wNameId);
        } else {
            SetLastError(0);
            _MBSTOWCS(szwNameId, szNameId, 128 );
            // Check for error
            if(GetLastError()) {
                FreeLibrary(hDllInst);
                return ERROR_DLL_LOAD;
            }
            lpUpdRes = (LPWSTR) &szwNameId[0];
            lpRes = &szNameId[0];
        }

        dwImageBufSize = (*lpfnGetImage)(  hResFileModule,
                                        lpType,
                                        lpRes,
                                        (DWORD)LOWORD(dwLang),
                                        lpImageBuf,
                                        dwSize
                                        );
        if (dwImageBufSize>dwSize ) {
            // The buffer is too small
            delete []lpImageBuf;
            lpImageBuf = new BYTE[dwImageBufSize];
            dwUpdSize = (*lpfnGetImage)(  hResFileModule,
                                            lpType,
                                            lpRes,
                                            (DWORD)LOWORD(dwLang),
                                            lpImageBuf,
                                            dwImageBufSize
                                           );
            if ((dwUpdSize-dwImageBufSize)!=0 ) {
                delete []lpImageBuf;
                lpImageBuf = LPNULL;
            }
        }else if (dwImageBufSize==0){
             delete []lpImageBuf;
             lpImageBuf = LPNULL;
        }

        SetLastError(0);

        TRACE1("\t\tUpdateResourceW: %d\n", (WORD)dwUpdLang);

        if(!UpdateResourceW( hUpd,
                             lpUpdType,
                             lpUpdRes,
                             HIWORD(dwLang),
                             (LPVOID)lpImageBuf,
                             dwImageBufSize ))
        {
            dwLstErr = GetLastError();
        }

        if (lpImageBuf) delete []lpImageBuf;
    }

    SetLastError(0);
    EndUpdateResourceW( hUpd, FALSE );

    dwLstErr = GetLastError();

    if (dwLstErr)
        dwLstErr +=LAST_ERROR;

    // Fix the check sum
    DWORD error;
    if(error = FixCheckSum(lpszTgtFilename,lpszSrcFilename, lpszSymbolPath))
        dwLstErr = error;

    delete lpStartBuf;
	FreeLibrary(hDllInst);

    return (UINT)dwLstErr;
}

extern "C"
DllExport
UINT
APIENTRY
RWUpdateImageEx(
    LPCSTR  lpszType,
    LPVOID  lpNewBuf,
    DWORD   dwNewSize,
    LPVOID  lpOldImage,
    DWORD   dwOldImageSize,
    LPVOID  lpNewImage,
    DWORD*  pdwNewImageSize,
    LPCSTR  lpRCFilename
    )
{
    UINT uiError = ERROR_NO_ERROR;

    // The Type we can parse are only the standard ones
    switch ((UINT)LOWORD(lpszType)) {

        case 4:
            uiError = UpdateMenu( lpNewBuf, dwNewSize,
                                  lpOldImage, dwOldImageSize,
                                  lpNewImage, pdwNewImageSize );
        break;

        case 5:
            uiError = UpdateDialog( lpNewBuf, dwNewSize,
                                  lpOldImage, dwOldImageSize,
                                  lpNewImage, pdwNewImageSize );
        break;

        case 6:
            uiError = UpdateString( lpNewBuf, dwNewSize,
                                    lpOldImage, dwOldImageSize,
                                    lpNewImage, pdwNewImageSize );
        break;

        case 9:
            uiError = UpdateAccel( lpNewBuf, dwNewSize,
                                   lpOldImage, dwOldImageSize,
                                   lpNewImage, pdwNewImageSize );
        break;

        case 11:
            uiError = UpdateMsgTbl( lpNewBuf, dwNewSize,
                                  lpOldImage, dwOldImageSize,
                                  lpNewImage, pdwNewImageSize );
        break;

        case 16:
            uiError = UpdateVerst( lpNewBuf, dwNewSize,
                                   lpOldImage, dwOldImageSize,
                                   lpNewImage, pdwNewImageSize );
        break;

        default:
            //
            // Get the file name from the iodll
            //
            if(lpRCFilename && strcmp(lpRCFilename, ""))
            {
                // try to Load the dll
                HINSTANCE hRCDllInst = LoadLibrary(lpRCFilename);
                if (hRCDllInst)
                {
                    UINT (FAR PASCAL * lpfnGenerateImageEx)(LPCSTR, LPVOID, DWORD, LPVOID, DWORD, LPVOID, DWORD*, LPCSTR);

                    lpfnGenerateImageEx = (UINT (FAR PASCAL *)(LPCSTR, LPVOID, DWORD, LPVOID, DWORD, LPVOID, DWORD*, LPCSTR))
                                                GetProcAddress( hRCDllInst, "RWUpdateImageEx" );

                    if (lpfnGenerateImageEx)
                    {
                        uiError = (*lpfnGenerateImageEx)( lpszType,
                                            lpNewBuf,
                                            dwNewSize,
                                            lpOldImage,
                                            dwOldImageSize,
                                            lpNewImage,
                                            pdwNewImageSize,
                                            NULL );
                    }
                    else
                    {
                        *pdwNewImageSize = 0L;
                        uiError = ERROR_RW_NOTREADY;
                    }

                    FreeLibrary(hRCDllInst);
                }
                else
                {
                    *pdwNewImageSize = 0L;
                    uiError = ERROR_RW_NOTREADY;
                }
            }
            else
            {
                *pdwNewImageSize = 0L;
                uiError = ERROR_RW_NOTREADY;
            }
        break;
    }

    return uiError;
}

extern "C"
DllExport
UINT
APIENTRY
RWUpdateImage(
    LPCSTR  lpszType,
    LPVOID  lpNewBuf,
    DWORD   dwNewSize,
    LPVOID  lpOldImage,
    DWORD   dwOldImageSize,
    LPVOID  lpNewImage,
    DWORD*  pdwNewImageSize
    )
{
    return RWUpdateImageEx(lpszType, lpNewBuf, dwNewSize,
            lpOldImage, dwOldImageSize, lpNewImage, pdwNewImageSize,
            NULL);
}

///////////////////////////////////////////////////////////////////////////
// Functions implementation
static UINT
GetResInfo( CFile* pfile,
            WORD* pwTypeId, LPSTR lpszTypeId, BYTE bMaxTypeLen,
            WORD* pwNameId, LPSTR lpszNameId, BYTE bMaxNameLen,
            WORD* pwFlags,
            DWORD* pdwSize, DWORD* pdwFileOffset )
{
    // Here we will parese the win32 file and will extract the information on the
    // resources included in the file.
    // Let's go and get the .rsrc sections
    UINT uiError = ERROR_NO_ERROR;

    return 1;
}

static UINT FindResourceSection( CFile* pfile, ULONG_PTR * pRes )
{
    UINT uiError = ERROR_NO_ERROR;
    LONG lRead;

    // We check again that is a file we can handle
    WORD w;

    pfile->Read((WORD*)&w, sizeof(WORD));
    if (w!=IMAGE_DOS_SIGNATURE) return ERROR_RW_INVALID_FILE;

    pfile->Seek( 0x18, CFile::begin );
    pfile->Read((WORD*)&w, sizeof(WORD));
    if (w<0x0040) {
    // this is not a Windows Executable
        return ERROR_RW_INVALID_FILE;
    }

    // get offset to new header
    pfile->Seek( 0x3c, CFile::begin );
    pfile->Read((WORD*)&w, sizeof(WORD));

    // read windows new header
    static IMAGE_NT_HEADERS NTHdr;
    pfile->Seek( w, CFile::begin );

    pfile->Read(&NTHdr, sizeof(IMAGE_NT_HEADERS));

    // Check if the magic word is the right one
    if (NTHdr.Signature!=IMAGE_NT_SIGNATURE)
                return ERROR_RW_INVALID_FILE;

    // Check if the we have 64-bit image
#ifdef _WIN64
    if (NTHdr.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
        pfile->Seek(IMAGE_SIZEOF_NT_OPTIONAL32_HEADER - 
                    IMAGE_SIZEOF_NT_OPTIONAL64_HEADER, 
                    CFile::current);
#else
    if (NTHdr.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
        pfile->Seek(IMAGE_SIZEOF_NT_OPTIONAL64_HEADER - 
                    IMAGE_SIZEOF_NT_OPTIONAL32_HEADER, 
                    CFile::current);
#endif

    // this is a Windows NT Executable
    // we can handle the situation

    // Later we want to check for the file type

    // Read the section table
    UINT uisize = sizeof(IMAGE_SECTION_HEADER)
          * NTHdr.FileHeader.NumberOfSections;
    PIMAGE_SECTION_HEADER pSectTbl =
            new IMAGE_SECTION_HEADER[NTHdr.FileHeader.NumberOfSections];

    if (pSectTbl==LPNULL)
    return ERROR_NEW_FAILED;

    // Clean the memory we allocated
    memset( (PVOID)pSectTbl, 0, uisize);

    lRead = pfile->Read(pSectTbl, uisize);

    if (lRead!=(LONG)uisize) {
        delete []pSectTbl;
        return ERROR_FILE_READ;
    }

    PIMAGE_SECTION_HEADER pResSect     = NULL;
    PIMAGE_SECTION_HEADER pResSect1    = NULL;
    // Check all the sections for the .rsrc or .rsrc1
    USHORT us =0;
    for (PIMAGE_SECTION_HEADER pSect = pSectTbl;
         us < NTHdr.FileHeader.NumberOfSections; us++ )     {
        if ( !strcmp((char*)pSect->Name, ".rsrc") && (!pResSect)) {
            pResSect = pSect;
        } else if (!strcmp((char*)pSect->Name, ".rsrc1") && (!pResSect1)) {
            // This mean that the binary we are parsing
            // has been already updated using UpdateResource()
            pResSect1 = pSect;
        }
        pSect++;
    }

    if (!pResSect) {
        delete []pSectTbl;
        return ERROR_RW_NO_RESOURCES;
    }
    // Read the resources in memory
    ResSectData.ulOffsetToResources  = pResSect->PointerToRawData;
    ResSectData.ulOffsetToResources1 = pResSect1 ? pResSect1->PointerToRawData
                                       : LPNULL;

    ResSectData.ulVirtualAddress   = pResSect->VirtualAddress;
    ResSectData.ulSizeOfResources  = pResSect->SizeOfRawData;
    ResSectData.ulVirtualAddress1  = pResSect1 ? pResSect1->VirtualAddress
                                           : LPNULL;
    ResSectData.ulSizeOfResources1 = pResSect1 ? pResSect1->SizeOfRawData
                                           : 0L;
    UCHAR * pResources = (UCHAR *) malloc((ResSectData.ulSizeOfResources
                  +ResSectData.ulSizeOfResources1));

    if (pResources==LPNULL) {
        delete []pSectTbl;
        return ERROR_NEW_FAILED;
    }

    // We read the data for the first section
    pfile->Seek( (LONG)ResSectData.ulOffsetToResources, CFile::begin);
    lRead = ReadFile(pfile, pResources, (LONG)ResSectData.ulSizeOfResources);

    if (lRead!=(LONG)ResSectData.ulSizeOfResources) {
        delete []pSectTbl;
        free(pResources);
        return ERROR_FILE_READ;
    }

    // We read the data for the second section
    if (ResSectData.ulSizeOfResources1 > 0L) {
        pfile->Seek( (LONG)ResSectData.ulOffsetToResources1, CFile::begin);
        lRead = ReadFile( pfile, (pResources+ResSectData.ulSizeOfResources),
                              (LONG)ResSectData.ulSizeOfResources1);

        if (lRead!=(LONG)ResSectData.ulSizeOfResources1) {
            delete []pSectTbl;
            free(pResources);
            return ERROR_FILE_READ;
        }
    }

    delete []pSectTbl;
    // We want to copy the pointer to the resources
    *pRes = (ULONG_PTR)pResources;
    return uiError;
}

static UINT ParseDirectory( CFile* pfile,
            LPLPBYTE lplpBuf, UINT* puiBufSize,
            BYTE bLevel,
            PIMAGE_RESOURCE_DIRECTORY pResStart,
            PIMAGE_RESOURCE_DIRECTORY pResDir)
{
    PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDirStart;

    // Get the pointer to the first entry
    pResDirStart = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)
            ((BYTE far *)pResDir + sizeof( IMAGE_RESOURCE_DIRECTORY));

    UINT uiError = 0;
    UINT uiCount = pResDir->NumberOfNamedEntries
             + pResDir->NumberOfIdEntries;

    for ( PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDirEntry = pResDirStart;
      pResDirEntry < pResDirStart+uiCount && uiError == 0;
          ++pResDirEntry )
    {
        if (bLevel==0) GetNameOrOrdU( (PUCHAR) pResStart,
                            pResDirEntry->Name,
                            (LPWSTR)&gwszTypeId,
                            &gType );
        if (bLevel==1) GetNameOrOrdU( (PUCHAR) pResStart,
                            pResDirEntry->Name,
                            (LPWSTR)&gwszResId,
                            &gResId );
        if (bLevel==2) gLng = pResDirEntry->Name;

        // Check if the user want to get all the resources
        // or only some of them
        uiError = ParseDirectoryEntry( pfile,
                lplpBuf, puiBufSize,
                bLevel,
            pResStart,
            pResDirEntry );
    }
    return uiError;
}

static UINT ParseDirectoryEntry( CFile * pfile,
            LPLPBYTE lplpBuf, UINT* puiBufSize,
            BYTE bLevel,
            PIMAGE_RESOURCE_DIRECTORY pResStart,
            PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDirEntry)
{
    UINT uiError;

    // Check if it is a SubDir or if it is a final Node
    if (pResDirEntry->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) {
        // It is a SubDir
        uiError = ParseSubDir( pfile,
            lplpBuf, puiBufSize,
            bLevel,
            pResStart,
            pResDirEntry );

    } else {
        uiError = ProcessData( pfile,
                    lplpBuf, puiBufSize,
                    pResStart,
                    (PIMAGE_RESOURCE_DATA_ENTRY)((BYTE far *)pResStart
                    + pResDirEntry->OffsetToData));
    }
    return uiError;
}

static UINT ParseSubDir( CFile * pfile,
            LPLPBYTE lplpBuf, UINT* puiBufSize,
            BYTE bLevel,
            PIMAGE_RESOURCE_DIRECTORY pResStart,
            PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDirEntry)
{
    PIMAGE_RESOURCE_DIRECTORY pResDir;

    pResDir = (PIMAGE_RESOURCE_DIRECTORY)((BYTE far *)pResStart
          + (pResDirEntry->OffsetToData &
            (~IMAGE_RESOURCE_DATA_IS_DIRECTORY)));

    return( ++bLevel < MAXLEVELS ? ParseDirectory( pfile,
                                        lplpBuf, puiBufSize,
                        bLevel,
                                    pResStart,
                                    pResDir)
                         : ERROR_RW_TOO_MANY_LEVELS);
}

static UINT ProcessData( CFile * pfile,
                         LPLPBYTE lplpBuf, UINT* puiBufSize,
                         PIMAGE_RESOURCE_DIRECTORY pResStart,
                         PIMAGE_RESOURCE_DATA_ENTRY pResData)
{
    UINT uiError = ERROR_NO_ERROR;

    // Let's calculate the offset to the data
    ULONG ulOffset = pResData->OffsetToData - ResSectData.ulVirtualAddress;

    if ( ulOffset >= ResSectData.ulSizeOfResources ) {
        if ( ResSectData.ulSizeOfResources1 > 0L )      {
            // What we need is in the .rsrc1 segment
            // Recalculate the offset;
            ulOffset = pResData->OffsetToData - ResSectData.ulVirtualAddress1;
            if ( ulOffset >= ResSectData.ulSizeOfResources +
                             ResSectData.ulSizeOfResources1) {
                // There is an error in the offset
                return ERROR_FILE_INVALID_OFFSET;
            } else ulOffset += ResSectData.ulOffsetToResources1;
        } else return ERROR_FILE_INVALID_OFFSET;
    } else ulOffset += ResSectData.ulOffsetToResources;

    // Convert the UNICODE to SB string
    static char szResName[128];
    UINT cch = _WCSLEN(gwszResId);
    _WCSTOMBS( szResName, gwszResId, 128 );

    static char szTypeName[128];
    cch = _WCSLEN(gwszTypeId);
    _WCSTOMBS( szTypeName, gwszTypeId, 128 );


    TRACE("WIN32.DLL:\tType: %ld\tType Name: %s\tLang: %ld\tRes Id: %ld", gType, szTypeName, gLng, gResId);
    TRACE1("\tSize: %d", pResData->Size);
    TRACE2("\tRes Name: %s\tOffset: %lX\n", szResName, ulOffset );

    // fill the buffer

    WriteResInfo(lplpBuf, (LONG*)puiBufSize,
                 (WORD)gType, szTypeName, 128,
                 (WORD)gResId, szResName, 128,
                 (DWORD)gLng,
                 (DWORD)pResData->Size, (DWORD)ulOffset );
    return uiError;
};

static LONG WriteResInfo(
                 LPLPBYTE lplpBuffer, LONG* plBufSize,
                 WORD wTypeId, LPSTR lpszTypeId, BYTE bMaxTypeLen,
                 WORD wNameId, LPSTR lpszNameId, BYTE bMaxNameLen,
                 DWORD dwLang,
                 DWORD dwSize, DWORD dwFileOffset )
{
    LONG lSize = 0;
    lSize = PutWord( lplpBuffer, wTypeId, plBufSize );
    lSize += PutStringA( lplpBuffer, lpszTypeId, plBufSize );
	 // Check if it is alligned
    lSize += Allign( lplpBuffer, plBufSize, lSize);

    lSize += PutWord( lplpBuffer, wNameId, plBufSize );
    lSize += PutStringA( lplpBuffer, lpszNameId, plBufSize );
    lSize += Allign( lplpBuffer, plBufSize, lSize);

    lSize += PutDWord( lplpBuffer, dwLang, plBufSize );

    lSize += PutDWord( lplpBuffer, dwSize, plBufSize );

    lSize += PutDWord( lplpBuffer, dwFileOffset, plBufSize );

    return (LONG)lSize;
}

static UINT GetUpdatedRes(
                 BYTE far * far* lplpBuffer,
                 UINT* puiBufSize,
                 WORD* wTypeId, LPSTR lplpszTypeId,
                 WORD* wNameId, LPSTR lplpszNameId,
                 DWORD* dwLang, DWORD* dwSize )
{
    UINT uiSize = 0l;
	LONG lSize = *puiBufSize;

    uiSize = GetWord( lplpBuffer, wTypeId, (LONG*)&lSize );
    uiSize += GetStringA( lplpBuffer, lplpszTypeId, (LONG*)&lSize );
	uiSize += SkipByte( lplpBuffer, PadPtr(uiSize), (LONG*)&lSize );

    uiSize += GetWord( lplpBuffer, wNameId, (LONG*)&lSize );
    uiSize += GetStringA( lplpBuffer, lplpszNameId, (LONG*)&lSize );
	uiSize += SkipByte( lplpBuffer, PadPtr(uiSize), (LONG*)&lSize );

    uiSize += GetDWord( lplpBuffer, dwLang, (LONG*)&lSize );

    uiSize += GetDWord( lplpBuffer, dwSize, (LONG*)&lSize );

	*puiBufSize = lSize;

    return 0;
}

static UINT GetRes(
                 BYTE far * far* lplpBuffer,
                 UINT* puiBufSize,
                 WORD* wTypeId, LPSTR lplpszTypeId,
                 WORD* wNameId, LPSTR lplpszNameId,
                 DWORD* dwLang, DWORD* dwSize, DWORD* dwFileOffset )
{
    UINT uiSize = 0l;
	 LONG lSize = *puiBufSize;

    uiSize = GetWord( lplpBuffer, wTypeId, (LONG*)&lSize );
    uiSize += GetStringA( lplpBuffer, lplpszTypeId, (LONG*)&lSize );
	 uiSize += SkipByte( lplpBuffer, PadPtr(uiSize), (LONG*)&lSize );

    uiSize += GetWord( lplpBuffer, wNameId, (LONG*)&lSize );
    uiSize += GetStringA( lplpBuffer, lplpszNameId, (LONG*)&lSize );
	 uiSize += SkipByte( lplpBuffer, PadPtr(uiSize), (LONG*)&lSize );

    uiSize += GetDWord( lplpBuffer, dwLang, (LONG*)&lSize );

    uiSize += GetDWord( lplpBuffer, dwSize, (LONG*)&lSize );

    uiSize += GetDWord( lplpBuffer, dwFileOffset, (LONG*)&lSize );

	 *puiBufSize = lSize;
    return uiSize;
}

static LONG ReadFile(CFile* pFile, UCHAR * pBuf, LONG lRead)
{
    LONG lLeft = lRead;
    WORD wRead = 0;
    DWORD dwOffset = 0;

    while(lLeft>0){
        wRead =(WORD) (32738ul < lLeft ? 32738: lLeft);
        if (wRead!=_lread( (HFILE)pFile->m_hFile, (UCHAR *)pBuf+dwOffset, wRead))
            return 0l;
        lLeft -= wRead;
        dwOffset += wRead;
    }
    return dwOffset;

}


////////////////////////////////////////////////////////////////////////////
// DLL Specific code implementation

////////////////////////////////////////////////////////////////////////////
// Library init

////////////////////////////////////////////////////////////////////////////
// This function should be used verbatim.  Any initialization or termination
// requirements should be handled in InitPackage() and ExitPackage().
//
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		// NOTE: global/static constructors have already been called!
		// Extension DLL one-time initialization - do not allocate memory
		// here, use the TRACE or ASSERT macros or call MessageBox
		AfxInitExtensionModule(extensionDLL, hInstance);
	}
	else if (dwReason == DLL_PROCESS_DETACH)
	{
		// Terminate the library before destructors are called
		AfxWinTerm();
	}

	if (dwReason == DLL_PROCESS_DETACH || dwReason == DLL_THREAD_DETACH)
		return 0;		// CRT term	Failed

	return 1;   // ok
}

/////////////////////////////////////////////////////////////////////////////