//..................................................................... //... //... 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 #include #include #include #include #include #include #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 += __max(usDeltaBlockSize, (USHORT)(usMsgBlkLen + usLen)); 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 = (USHORT)_MBSTOWCS( szwString, puchStart, WCHARSIN( sizeof( szwString)), ACHARSIN( lstrlenA( puchStart) + 1)); if (usLen == 0) QuitT( IDS_ENGERR_10, szwString, NULL); pszwStart = szwString; } else //... Unicode message { pszwStart = (WCHAR *)(((PMRE)pMRE)->Text); usLen = (USHORT)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); } }