//.....................................................................
//...
//... NTMSGTBL.C
//...
//... Contains functions for handling strings found in NT's Message
//... Resource Tables.  This recource type is not present in Win 3.1.
//...
//... Author - David Wilcox (davewi@microsoft)
//...
//... NOTES:  Created with tabstop set to 8
//...
//.....................................................................
//...
//... History:
//... Original - 10/92
//...            11/92 - Fixed to handle ULONG msg ID#'s - davewi
//...
//.....................................................................

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windef.h>
#include <tchar.h>
#include <winver.h>

#include "windefs.h"
#include "restok.h"
#include "custres.h"
#include "ntmsgtbl.h"
#include "resread.h"


typedef PMESSAGE_RESOURCE_ENTRY PMRE;

extern BOOL  gbMaster;
extern UCHAR szDHW[];

static PBYTE *pBlockEntries = NULL;

VOID  *pResMsgData = NULL;      // NT-specific Message Table resource


//.........................................................................
//...
//... Get Message Table from .res file
//...
//... This form of a message table, not found in Win 16, allows very long
//... strings and the text is stored as an ASCIIZ string in the .res file.

    
VOID *GetResMessage(

FILE  *pInResFile,      //... The file containing the resources
DWORD *plSize)          //... The size of this resource from GetResHeader
{
    ULONG  ulNumBlocks = 0L;            //... # of Message Table resource blocks
    ULONG  ulStartMsgDataPos = 0L;      //... Start of message data in file
    ULONG  ulBlock;                     //... Current message block number
    USHORT usCurrBlockSize  = 0;        //... Current size of temp block buffer
    USHORT usDeltaBlockSize = 4096;     //... Amount to increase usCurrBlockSize
    DWORD  dwNumMsgs = 0;               //... Count of msgs in the resource
    PBYTE  pMsgBlock = NULL;            //... Temp message block buffer

    PMESSAGE_RESOURCE_DATA  pMsgResData;//... Returned as ptr to the resource
    PMESSAGE_RESOURCE_BLOCK pMRB;       //... ptr to a block of messages



                                //... The resource header was read prior to
                                //... entring this function, so the current
                                //... file position should now be the start
                                //... of the resource data.
    
    ulStartMsgDataPos = ftell( pInResFile);
    
                                //... Get the number of message blocks and
                                //... allocate enough memory for the array.
    
    ulNumBlocks = GetdWord( pInResFile, plSize);
    
                                //... Allocate space for the array of
                                //... pointers to entries.  This array is used
                                //... to store pointers to the first entry
                                //... in each block of message entries.
    
    pBlockEntries = (PBYTE *)FALLOC( ulNumBlocks * sizeof( PBYTE));

    if ( ! pBlockEntries )
    {
        QuitT( IDS_ENGERR_11, NULL, NULL);
    }

    pMsgResData = (PMESSAGE_RESOURCE_DATA)FALLOC( sizeof( ULONG) + ulNumBlocks
                                                   * sizeof( MESSAGE_RESOURCE_BLOCK));

    if ( ! pMsgResData )
    {
        QuitT( IDS_ENGERR_11, NULL, NULL);
    }
    pResMsgData = pMsgResData;
    pMsgResData->NumberOfBlocks = ulNumBlocks;
    
                                //... Read the array of message block structs,
                                //... and initialize block entry pointer array.
    
    for ( ulBlock = 0L, pMRB = pMsgResData->Blocks;
          ulBlock < ulNumBlocks;
          ++ulBlock, ++pMRB )
    {
        pMRB->LowId           = GetdWord( pInResFile, plSize);
        pMRB->HighId          = GetdWord( pInResFile, plSize);
        pMRB->OffsetToEntries = GetdWord( pInResFile, plSize);

        if ( pMRB->HighId < pMRB->LowId )
        {
            ClearResMsg( &pResMsgData);
            QuitT( IDS_ENGERR_16, (LPTSTR)IDS_INVMSGRNG, NULL);
        }
        dwNumMsgs += (pMRB->HighId - pMRB->LowId + 1);
    
        pBlockEntries[ ulBlock] = NULL;
    }

                                //... Read in the MESSAGE_RESOURCE_ENTRY
    
    usCurrBlockSize = usDeltaBlockSize;

    for ( ulBlock = 0L, pMRB = pMsgResData->Blocks;
          ulBlock < ulNumBlocks;
          ++ulBlock, ++pMRB )
    {
        ULONG   ulCurrID;       //... Current message ID # in this block
        ULONG   ulEndID;        //... Last message ID # in this block + 1
        USHORT  usLen;          //... For length of a message - MUST BE USHORT
        USHORT  usMsgBlkLen;    //... Length of a block of messages
    
    
        usMsgBlkLen = 0;
    
                                //... Move to start of block of message entries
                                //... then read all the messages in this block.
    
        fseek( pInResFile,
               ulStartMsgDataPos + pMRB->OffsetToEntries,
               SEEK_SET);
    
        for ( ulCurrID = pMRB->LowId, ulEndID = pMRB->HighId + 1;
              ulCurrID < ulEndID;
              ++ulCurrID, --dwNumMsgs )
        {
                                //... Get Msg Resource entry length
                                //... (Length is in bytes and includes
                                //...  .Length and .Flags fields and any
                                //...  padding that may exist after the text.)
        
            usLen = GetWord( pInResFile, plSize);

            if ( usLen >= 2 * sizeof( USHORT) )
            {
                PMRE   pMRE;
                PUCHAR puchText;
        
                                //... Create, or expand size of, pMsgBlkData
                                //... so we can append this entry.
                                //... Always resave ptr to the message block
                                //... (it may have moved).

                if ( pMsgBlock )
                {
                    if ( (USHORT)(usMsgBlkLen + usLen) > usCurrBlockSize )
                    {
                        usCurrBlockSize += usDeltaBlockSize;
            
                        pMsgBlock = (PBYTE)FREALLOC( pMsgBlock,
                                                      usCurrBlockSize);
                    }
                }
                else
                {
                    pMsgBlock = FALLOC( usCurrBlockSize);
                }
        
                                //... If the malloc worked, read this msg entry.
                                //... The section assumes there is one WORD
                                //... per USHORT and one WORD per WCHAR.
        
                pMRE = (PMRE)(pMsgBlock + usMsgBlkLen);
        
                                //... Store the .Length field value (USHORT)
        
                pMRE->Length = usLen;
                usMsgBlkLen += usLen;
        
                                //... Get the .Flags field value (USHORT)
        
                pMRE->Flags = GetWord( pInResFile, plSize);

                                //... Check to make sure this message is stored
                                //... either in ASCII in the current code page
                                //... or in Unicode, else fail.
                                
                if ( pMRE->Flags != 0                           //... ASCII
                  && pMRE->Flags != MESSAGE_RESOURCE_UNICODE )  //... Unicode
                {
                    if ( pMsgBlock != NULL )
                    {
                        RLFREE( pMsgBlock);
                    }
                    ClearResMsg( &pResMsgData);
                    QuitA( IDS_NON0FLAG, NULL, NULL);
                }
        
                                //... Get the .Text field string
        
                usLen -= (2 * sizeof( WORD));
        
                for ( puchText = (PUCHAR)pMRE->Text; usLen; ++puchText, --usLen )
                {
                    *puchText = (UCHAR)GetByte( pInResFile, plSize);
                }
                DWordUpFilePointer( pInResFile,
                                    MYREAD,
                                    ftell( pInResFile),
                                    plSize);
            }
            else
            {
                if ( pMsgBlock != NULL )
                {
                    RLFREE( pMsgBlock);
                }
                ClearResMsg( &pResMsgData);
                QuitT( IDS_ENGERR_05, (LPTSTR)IDS_INVMSGTBL, NULL);
            }
        }                       //... END FOR(each message entry in this block)
    
        if ( pMsgBlock != NULL && usMsgBlkLen > 0 )
        {
            pBlockEntries[ ulBlock] = FALLOC( usMsgBlkLen);
        
            memcpy( pBlockEntries[ ulBlock], pMsgBlock, usMsgBlkLen);
        }
    }                           //... END FOR(each message block)

    if ( pMsgBlock != NULL )
    {
        RLFREE( pMsgBlock);
    }

    DWordUpFilePointer( pInResFile, MYREAD, ftell( pInResFile), plSize);
    
    return( (VOID *)pMsgResData);
}




//.........................................................................
//...
//... Put localized Message Table into .res
//...
//... 01/93 - changes for var length Token text.  MHotchin
//... 02/93 - stripped out code that split msgs into multiple tokens.  davewi

void PutResMessage(

FILE *fpOutResFile,     //... File to which localized resources are written
FILE *fpInTokFile,      //... Output token file
RESHEADER ResHeader,    //... Resource header data
VOID *pMsgResData)      //... message table data built in GetResMessage
{
    WORD   wcCount = 0;
    fpos_t ulResSizePos   = 0L; //... File position for fixed up resource size
    fpos_t ulBlocksStartPos=0L; //... File position of start of message blocks
    ULONG  ulNumBlocks    = 0L; //... Number of Message Blocks
    ULONG  ulCurrOffset   = 0L; //... Offset to current msg block
    ULONG  ulResSize      = 0L; //... Size of this resource
    ULONG  ulBlock;             //... Temporary counter
    USHORT usEntryLen = 0;      //... Length of current message entry
    PMESSAGE_RESOURCE_DATA pData; //. Message table data from InResFile
    static TOKEN  Tok;          //... Token from localized token file

    
    if ( pMsgResData == NULL)
    {
        QuitT( IDS_ENGERR_05, (LPTSTR)IDS_NULMSGDATA, NULL);
    }
    memset( (void *)&Tok, 0, sizeof( Tok));
    pData = (PMESSAGE_RESOURCE_DATA)pMsgResData;
    
    if ( PutResHeader( fpOutResFile, ResHeader, &ulResSizePos, &ulResSize))
    {
        ClearResMsg( &pResMsgData);
        QuitT( IDS_ENGERR_06, (LPTSTR)IDS_MSGTBLHDR, NULL);
    }

    ulResSize = 0L;             //... Reset to zero (hdr len not to be included)

    ulNumBlocks = pData->NumberOfBlocks;
    
                                //... Write number of msg blocks
    
    PutdWord( fpOutResFile, ulNumBlocks, &ulResSize);
    
                                //... Remember this file position so we can
                                //... come back here and update the
                                //... OffsetToEntries field in each struct.
    
    ulBlocksStartPos = ftell( fpOutResFile);
    
                                //... Write the array of message block structs
    
    for ( ulBlock = 0L; ulBlock < ulNumBlocks; ++ulBlock )
    {
        PutdWord( fpOutResFile, pData->Blocks[ ulBlock].LowId,  &ulResSize);
        PutdWord( fpOutResFile, pData->Blocks[ ulBlock].HighId, &ulResSize);
        PutdWord( fpOutResFile, 0L, &ulResSize);  //... Will get fixed up later
    }
                                // Prep for find token call
    
    Tok.wType = ResHeader.wTypeID;
    Tok.wName = ResHeader.wNameID;
    Tok.wID   = 0;
    Tok.wFlag = 0;
    
    if ( ResHeader.bNameFlag == IDFLAG )
    {
        lstrcpy( Tok.szName, ResHeader.pszName);
    }
                                //... Write the MESSAGE_RESOURCE_ENTRY's. First
                                //... note offset from start of this resource's
                                //... data to first msg res entry struct which
                                //... starts right after the array of
                                //... RESOURCE_MESSAGE_BLOCK structs.
    
    ulCurrOffset = sizeof( ULONG) + ulNumBlocks*sizeof( MESSAGE_RESOURCE_BLOCK);
    
    for ( ulBlock = 0L; ulBlock < ulNumBlocks; ++ulBlock )
    {
        ULONG   ulCurrID;       //... Current message ID # in this block
        ULONG   ulEndID;        //... Last message ID # in this block + 1
        fpos_t  ulEntryPos;     //... Start of the current msg entry struct
        PBYTE   pMRE;           //... Ptr to a MESSAGE_RESOURCE_ENTRY
        PMESSAGE_RESOURCE_BLOCK pMRB;
    
    
                                //... Retrieve ptr to block of messages.  The
                                //... ptr was stored in the pBlockEntries array
                                //... in GetResMessage function above.
    
        pMRB = (PMESSAGE_RESOURCE_BLOCK)( &pData->Blocks[ ulBlock]);
        pMRE = pBlockEntries[ ulBlock];
    
                                //... Note offset to start of block's entries
    
        pData->Blocks[ ulBlock].OffsetToEntries = ulCurrOffset;
    
        for ( ulCurrID = pMRB->LowId, ulEndID = pMRB->HighId + 1;
              ulCurrID < ulEndID;
              ++ulCurrID )
        {
            static UCHAR szString[ 64] = "";
            static TCHAR szwTmp[ 4096] = TEXT("");
            USHORT usCnt = 0;
            BOOL   fFound = FALSE;
            ULONG  ulEntrySize = 0;

        
            ulEntryPos  = ftell( fpOutResFile);
            ulEntrySize = 0L;
        
                                //... Write dummy entry length.
                                //... Value gets corrected later.
                                //... Write the .Flags field's value (USHORT).
        
            PutWord( fpOutResFile, ((PMRE)pMRE)->Length, &ulEntrySize);
            PutWord( fpOutResFile, ((PMRE)pMRE)->Flags,  &ulEntrySize);

                                //... Get localized token then the length of
                                //... that token's new text.  Add to that length
                                //... the length of the two USHORTs and use this
                                //... combined length as the value to store in
                                //... the msg res entry's .Length field.

                                //... Put low word of ID# in .wID and
                                //... the high word in .szName
        
            Tok.wID = LOWORD( ulCurrID);
            _itoa( HIWORD( ulCurrID), szString, 10);
            _MBSTOWCS( Tok.szName,
                       szString,
                       TOKENSTRINGBUFFER,
                       lstrlenA( szString) + 1);
        
                                //... Always reset .wReserved because the code
                                //... in FindTokenText will change its value.
        
            Tok.wReserved = ST_TRANSLATED;
        
            Tok.szText = NULL;
            *szwTmp  = TEXT('\0');

            for ( fFound = FALSE, Tok.wFlag = 0;
                  fFound = FindToken( fpInTokFile, &Tok, ST_TRANSLATED);
                  Tok.wFlag++ )
            {
                TextToBin( szwTmp, Tok.szText, lstrlen( Tok.szText) + 1);

                                //... Write out localized message text. It may
                                //... be stored as ASCII or Unicode string.

                if ( ((PMRE)pMRE)->Flags == 0 )  //... ASCII message
                {
                    _WCSTOMBS( szDHW,
                               szwTmp,
                               DHWSIZE,
                               lstrlen( szwTmp) + 1);

                    for ( usCnt = 0; szDHW[ usCnt]; ++usCnt )
                    {
                        PutByte( fpOutResFile, szDHW[ usCnt], &ulEntrySize);
                    }
                }
                else                            //... Unicode message
                {
                    for ( usCnt = 0; szwTmp[ usCnt]; ++usCnt )
                    {
                        PutWord( fpOutResFile, szwTmp[ usCnt], &ulEntrySize);
                    }
                }
                *szwTmp  = TEXT('\0');
                RLFREE( Tok.szText);

                                //... Always reset .wReserved because the code
                                //... in FindTokenText will change its value.
        
                Tok.wReserved = ST_TRANSLATED;
            }

                                //... Did we find the token?

            if ( Tok.wFlag == 0 && ! fFound )
            {
                static TCHAR szToken[ 4160];


                ParseTokToBuf( szToken, &Tok);
        
                ClearResMsg( &pResMsgData);
                QuitT( IDS_ENGERR_05, szToken, NULL);
            }
                                //... nul-terminate the text

            if ( ((PMRE)pMRE)->Flags == 0 )  //... ASCII message
            {
                PutByte( fpOutResFile , '\0', (DWORD *)&ulEntrySize);
            }
            else                            //... Unicode message
            {
                PutWord( fpOutResFile , TEXT('\0'), (DWORD *)&ulEntrySize);
            }
            DWordUpFilePointer( fpOutResFile,
                                MYWRITE,
                                ftell( fpOutResFile),
                                &ulEntrySize);
        
                                //... Also, use this length in later updating
                                //... next msg block's OffsetToEntries value.
        
            ulResSize    += ulEntrySize;
            ulCurrOffset += ulEntrySize;

                                //... Write Msg Resource entry length
                                //... (Length is in bytes and includes
                                //...  .Length and .Flags fields and any
                                //...  padding needed after the text.)
                                //...
                                //... NOTE: Msg text is currently stored as
                                //... an ASCIIZ string.
            fseek( fpOutResFile, (long)ulEntryPos, SEEK_SET);
        
            PutWord( fpOutResFile, (WORD)ulEntrySize, NULL);
        
            fseek( fpOutResFile, 0L, SEEK_END);
        
                                //... Move pMRE to point to start of next
                                //... Message Resource Entry in memory.
        
            pMRE += ((PMRE)pMRE)->Length;

        }                       //... END FOR(each message entry in this block)
    
        ulCurrOffset = DWORDUP( ulCurrOffset);
        DWordUpFilePointer( fpOutResFile,
                            MYWRITE,
                            ftell( fpOutResFile),
                            &ulResSize);
    
    }                           //... END FOR(each message block)
    
                                //... Update resource size field in res header
    
    if ( UpdateResSize( fpOutResFile, &ulResSizePos, ulResSize) == 0L )
    {
        ClearResMsg( &pResMsgData);
        QuitT( IDS_ENGERR_07, (LPTSTR)IDS_MSGRESTBL, NULL);
    }
                                //... Now, update the OffsetToEntries fields.
    
    fseek( fpOutResFile, (long)ulBlocksStartPos, SEEK_SET);
    
    for ( ulBlock = 0L; ulBlock < ulNumBlocks; ++ulBlock )
    {
        PutdWord( fpOutResFile, pData->Blocks[ulBlock].LowId,           NULL);
        PutdWord( fpOutResFile, pData->Blocks[ulBlock].HighId,          NULL);
        PutdWord( fpOutResFile, pData->Blocks[ulBlock].OffsetToEntries, NULL);
    }
    fseek( fpOutResFile, 0L, SEEK_END);
    
}       //... END PutResMessage()




//.........................................................................
//...
//... Write Message Table to the token file
//...
//... This function assumes that, in each message block, the message ID's are
//... contiguouse within the range given in the fields LowId and HighId in a
//... MESSAGE_RESOURCE_BLOCK.
//
// 01/93 - Changes for var length token text strings.  Mhotchin
//

void TokResMessage(

FILE      *pfTokFile,       //... Output token file
RESHEADER  ResHeader,       //... Resource header data
VOID      *pMsgResData)     //... Data to tokenize (from GetResMessage call)
{
    static TOKEN Tok;
    ULONG  ulBlock;                 //... Message resource block number
    PMESSAGE_RESOURCE_DATA  pData;  //... Data to tokenize
    PMESSAGE_RESOURCE_BLOCK pMRB;   //... ptr to a message block struct
    
    
    pData = (PMESSAGE_RESOURCE_DATA)pMsgResData;
    memset( (void *)&Tok, 0, sizeof( Tok));
    
    Tok.wType = ResHeader.wTypeID;
    Tok.wName = ResHeader.wNameID;
    
    Tok.wReserved = (gbMaster ? ST_NEW : ST_NEW | ST_TRANSLATED);
    
    if ( ResHeader.bNameFlag == IDFLAG )
    {
        lstrcpy( Tok.szName, ResHeader.pszName);
    }
    
    for ( ulBlock = 0L; ulBlock < pData->NumberOfBlocks; ++ulBlock )
    {
        ULONG  ulCurrID  = 0L;  //... ID # of current msg being processed
        ULONG  ulEndID;         //... Last message ID # in this block + 1
        USHORT usLineNum = 0;   //... Count of lines in a message text
        PCHAR  pMRE;            //... ptr to a message entry struct
    
    
                                //... Get ptr to this message block struct
    
        pMRB = &pData->Blocks[ ulBlock];
    
                                //... Get ptr to first entry in
                                //... this block of messages
    
        pMRE = (PCHAR)pBlockEntries[ ulBlock];
    
                                //... Tokenize entries in this block of messages
    
        for ( ulCurrID = pMRB->LowId, ulEndID = pMRB->HighId + 1;
              ulCurrID < ulEndID;
              ++ulCurrID )
        {
            usLineNum = 0;
        
                                //... inclusive of .Length and .Flags fields so
                                //... we need get the real length of the text.
        
            if ( ((PMRE)pMRE)->Length >= 2 * sizeof( WORD) )
            {
                USHORT usLen        = 0;
                USHORT usTokTextLen = 0;
                PWCHAR pszwStart = NULL;
                                // This is really ugly.  This code was
                                // originally to get around the problem
                                // that tokens could hold only 260 chars.
                                // Now, it's whatever you want.
                                // Temp hack - assume each line will be
                                // less than 4k in size. (mhotchin)
                static TCHAR szwString[ 32768 ];

                                //... Put low word of ID# in .wID and
                                //... the high word in .szName
        
                Tok.wID = LOWORD( ulCurrID);
                _itoa( HIWORD( ulCurrID), szDHW, 10);
                _MBSTOWCS( Tok.szName,
                           szDHW,
                           TOKENSTRINGBUFFER,
                           lstrlenA( szDHW) +1);
        
                                //... The err msg table strings may be stored
                                //... in the resources as ANSI or Unicode.
                                //... If the pMRE->Flags field in the
                                //... table entry struct is 0, the text is a
                                //... ANSI striing so we need to convert it to
                                //... UNICODE (WCHAR).
        
                if ( ((PMRE)pMRE)->Flags == 0 ) //... ASCII message
                {
                    PUCHAR puchStart = (PUCHAR)((PMRE)pMRE)->Text;

                    usLen = _MBSTOWCS( szwString,
                                       puchStart,
                                       WCHARSIN( sizeof( szwString)),
                                       ACHARSIN( lstrlenA( puchStart) + 1));

		    if (usLen != lstrlenA(puchStart))
			QuitT( IDS_ENGERR_10, szwString, NULL);

                    pszwStart = szwString;
                }
                else                            //... Unicode message
                {
                    pszwStart = (WCHAR *)(((PMRE)pMRE)->Text);
                    usLen = lstrlen( pszwStart) /*+ 1*/;
                }
                                //... We need to split the token text at \r\n

                for ( Tok.wFlag = 0;
                      usLen > 0;
                      usLen -= usTokTextLen, Tok.wFlag++ )
                {
                    WCHAR wcTmp;


                    for ( usTokTextLen = 0, wcTmp = TEXT('\0');
                          usTokTextLen < usLen;
                        ++usTokTextLen )
                    {
                        if ( pszwStart[ usTokTextLen]   == TEXT('\r')
                          && pszwStart[ usTokTextLen+1] == TEXT('\n') )
                        {
                            usTokTextLen += 2;
                            wcTmp = pszwStart[ usTokTextLen];
                            pszwStart[ usTokTextLen] = TEXT('\0');

                            break;
                        }
                    }

                    Tok.szText = BinToTextW( pszwStart, usTokTextLen);

                    PutToken( pfTokFile, &Tok);

                    RLFREE( Tok.szText);

                    pszwStart += usTokTextLen;
                    *pszwStart = wcTmp;
                }
                //... Set up to move to start of next msg entry
        
                pMRE += ((PMRE)pMRE)->Length;
            }
            else
            {
                ClearResMsg( &pResMsgData);
                QuitT( IDS_ENGERR_05, (LPTSTR)IDS_MSGTOOSHORT, NULL);
            }
        }                       //... END FOR processing a msg block
    }                           //... END FOR processing all msg blocks
}





//.........................................................................
//...
//... Clear memory created in GetResMessage()

void ClearResMsg(

VOID **pData)      //... ptr to ptr to start of memory to free
{
    if ( pData != NULL && *pData != NULL )
    {
        ULONG                   ulBlock;
        PMESSAGE_RESOURCE_DATA  pMRD;   //... ptr to a message data struct
        PMESSAGE_RESOURCE_BLOCK pMRB;   //... ptr to a message block struct
    
    
        pMRD = (PMESSAGE_RESOURCE_DATA)*pData;
        pMRB = pMRD->Blocks;
    
        if ( pBlockEntries != NULL )
        {
            for ( ulBlock = 0L; ulBlock < pMRD->NumberOfBlocks; ++ulBlock )
            {
                if ( pBlockEntries[ ulBlock] )
                {
                    RLFREE( pBlockEntries[ ulBlock]);
                }
            }
            RLFREE( (PBYTE)pBlockEntries);
        }
        RLFREE( *pData);
    }
}