Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1435 lines
44 KiB

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <assert.h>
#include <io.h>
#include <md5.h>
#include <strsafe.h> // safe string
#include "muibld.h"
typedef struct
{
BOOL bContainResource;
MD5_CTX ChecksumContext;
pCommandLineInfo pInfo;
} CHECKSUM_ENUM_DATA;
void ExitFromOutOfMemory();
void ExitFromSafeStringError();
void DumpResourceDirectory
(
PIMAGE_RESOURCE_DIRECTORY resDir,
DWORD resourceBase,
DWORD level,
DWORD resourceType
);
int g_bVerbose = FALSE; // Global flag to contorl verbose output.
WORD wChecksumLangId;
// The predefined resource types
char *SzResourceTypes[] = {
"???_0", "CURSOR", "BITMAP", "ICON", "MENU", "DIALOG", "STRING", "FONTDIR",
"FONT", "ACCELERATORS", "RCDATA", "MESSAGETABLE", "GROUP_CURSOR",
"???_13", "GROUP_ICON", "???_15", "VERSION"
};
void PrintError()
{
LPTSTR lpMsgBuf;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
))
{
printf("GetLastError():\n %s", lpMsgBuf);
LocalFree( lpMsgBuf );
}
return;
}
////////////////////////////////////////////////////////////////////////////////////
//
// ChecksumEnumNamesFunc
//
// The callback funciton for enumerating the resource names in the specified module and
// type.
// The side effect is that MD5 checksum context (contained in CHECKSUM_ENUM_DATA
// pointed by lParam) will be updated.
//
// Return:
// Always return TRUE so that we can finish all resource enumeration.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChecksumEnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG_PTR lParam){
HRSRC hRsrc;
HGLOBAL hRes;
const unsigned char* pv;
LONG ResSize=0L;
WORD IdFlag=0xFFFF;
DWORD dwHeaderSize=0L;
CHECKSUM_ENUM_DATA* pChecksumEnumData = (CHECKSUM_ENUM_DATA*)lParam;
if(!(hRsrc=FindResourceEx(hModule, lpType, lpName, wChecksumLangId ? wChecksumLangId : 0x409)))
{
//
// If US English resource is not found for the specified type and name, we
// will continue the resource enumeration.
//
return (TRUE);
}
pChecksumEnumData->bContainResource = TRUE;
if (!(ResSize=SizeofResource(hModule, hRsrc)))
{
printf("WARNING: Can not get resource size when generating resource checksum.\n");
return (TRUE);
}
if (!(hRes=LoadResource(hModule, hRsrc)))
{
printf("WARNING: Can not load resource when generating resource checksum.\n");
return (TRUE);
}
pv=(unsigned char*)LockResource(hRes);
//
// Update MD5 context using the binary data of this particular resource.
//
MD5Update(&(pChecksumEnumData->ChecksumContext), pv, ResSize);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////
//
// ChecksumEnumTypesFunc
//
// The callback function for enumerating the resource types in the specified module.
// This function will call EnumResourceNames() to enumerate all resource names of
// the specified resource type.
//
// Return:
// TRUE if EnumResourceName() succeeds. Otherwise FALSE.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChecksumEnumTypesFunc(HMODULE hModule, LPSTR lpType, LONG_PTR lParam)
{
CHECKSUM_ENUM_DATA* pChecksumEnumData = (CHECKSUM_ENUM_DATA*)lParam;
//
// Skip the version resource type, so that version is not included in the resource checksum.
//
if (lpType == RT_VERSION)
{
return (TRUE);
}
//
// If muibld include -i arg. but the lpType is not in the -i arg. values, it does not
// use lpType for checksum calculation.
//
if(pChecksumEnumData->pInfo->bIncludeFlag &&
!bTypeIncluded((char *)lpType, pChecksumEnumData->pInfo->pszIncResType)) {
return TRUE;
}
if(!EnumResourceNames(hModule, (LPCSTR)lpType, ChecksumEnumNamesFunc, (LONG_PTR)lParam))
{
return (FALSE);
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////////////
//
// GenerateResourceChecksum
//
// Generate the resource checksum for the US English resource in the specified file.
//
// Parameters:
// pszSourceFileName The file used to generate resource checksum.
// pResourceChecksum Pointer to a 16 bytes (128 bits) buffer for storing
// the calcuated MD5 checksum.
// pInfo pCommandLineInfo structure.the routine refer to
// pszChecksumFile, pResourceChecksum, pszIncResType.
// Return:
// TURE if resource checksum is generated from the given file. Otherwise FALSE.
//
// The following situation may return FALSE:
// * The specified file does not contain resource.
// * If the specified file contains resource, but the resource is not US English.
// * The specified file only contains US English version resource.
//
// Note.
// muibld calculate the checksum only based of included resource types by -i arg.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL GenerateResourceChecksum(pCommandLineInfo pInfo) // LPCSTR pszSourceFileName, unsigned char* pResourceChecksum)
{
ULONG i;
HMODULE hModule = NULL;
DWORD dwResultLen;
BOOL bRet = FALSE;
//
// The stucture to be passed into the resource enumeration functions.
//
CHECKSUM_ENUM_DATA checksumEnumData;
checksumEnumData.bContainResource = FALSE;
checksumEnumData.pInfo = pInfo;
//
// Start MD5 checksum calculation by initializing MD5 context.
//
MD5Init(&(checksumEnumData.ChecksumContext));
// Info.pszChecksumFile, Info.pResourceChecksum
if (g_bVerbose)
{
printf("Generate resource checksum for [%s]\n", pInfo->pszChecksumFile);
}
if(!(hModule = LoadLibraryEx(pInfo->pszChecksumFile, NULL, DONT_RESOLVE_DLL_REFERENCES|LOAD_LIBRARY_AS_DATAFILE)))
{
if (g_bVerbose)
{
printf("\nERROR: Error in opening resource checksum module [%s]\n", pInfo->pszChecksumFile);
}
PrintError();
goto GR_EXIT;
}
if (g_bVerbose)
{
printf("\nLoad checksum file: %s\n", pInfo->pszChecksumFile);
}
//
// we check language id of Version resource if it has wChecksumLangId when wChecksumLangId has value
//
if (wChecksumLangId && wChecksumLangId != 0x409)
{
if(!FindResourceEx(hModule, MAKEINTRESOURCE(16), MAKEINTRESOURCE(1), wChecksumLangId))
{ //
// It does not has specifed language id in version resource, we supposed that this binary does not
// have any language id specified at all, so we set it as 0 in order to use English instead.
//
wChecksumLangId = 0;
}
}
//
// Enumerate all resources in the specified module.
// During the enumeration, the MD5 context will be updated.
//
if (!EnumResourceTypes(hModule, ChecksumEnumTypesFunc, (LONG_PTR)&checksumEnumData))
{
if (g_bVerbose)
{
printf("\nWARNING: Unable to generate resource checksum from resource checksum module [%s]\n", pInfo->pszChecksumFile);
}
goto GR_EXIT;
}
if (checksumEnumData.bContainResource)
{
//
// If the enumeration succeeds, and the specified file contains US English
// resource, get the MD5 checksum from the accumulated MD5 context.
//
MD5Final(&checksumEnumData.ChecksumContext);
memcpy(checksumEnumData.pInfo->pResourceChecksum, checksumEnumData.ChecksumContext.digest, 16);
if (g_bVerbose)
{
printf("Generated checksum: [");
for (i = 0; i < MD5_CHECKSUM_SIZE; i++)
{
printf("%02x ", pInfo->pResourceChecksum[i]);
}
printf("]\n");
}
bRet = TRUE;
}
GR_EXIT:
if (hModule)
{
FreeLibrary(hModule);
}
return (bRet);
}
int __cdecl main(int argc, char *argv[]){
struct CommandLineInfo Info;
HMODULE hModule=0;
char pszBuffer[400];
DWORD dwError;
DWORD dwOffset;
BOOL bEnumTypesReturn;
if(argc==1){
Usage();
return 0;
}
g_bVerbose=FALSE;
wChecksumLangId=0;
Info.pszIncResType=0;
Info.wLanguage=0;
Info.hFile=0;
Info.pszSource=0;
Info.pszTarget=0;
Info.bContainsOnlyVersion=TRUE;
Info.bContainsResources=FALSE;
Info.bLanguageFound=FALSE;
Info.bIncDependent=FALSE;
Info.bIncludeFlag=FALSE;
Info.pszChecksumFile=NULL;
Info.bIsResChecksumGenerated = FALSE;
if(ParseCommandLine(argc, argv, &Info)==FALSE){
//...If help was the only command line argument, exit
if(strcmp(argv[1], "-h")==0 && argc==2)
return 0;
dwError=ERROR_TOO_FEW_ARGUMENTS;
dwOffset=0;
goto Error_Exit;
}
//...Open resource module
if(Info.pszSource){
if(!(hModule = LoadLibraryEx (Info.pszSource, NULL, DONT_RESOLVE_DLL_REFERENCES|LOAD_LIBRARY_AS_DATAFILE)))
{
PrintError();
if (g_bVerbose)
{
printf("\nERROR: Error in opening source module [%s]\n", Info.pszSource);
}
dwError=GetLastError();
dwOffset=ERROR_OFFSET;
goto Error_Exit;
}
}
else {
Usage();
dwError=ERROR_NO_SOURCE;
dwOffset=0;
goto Error_Exit;
}
if (Info.pszChecksumFile)
{
if (GenerateResourceChecksum(&Info)) // c))
{
Info.bIsResChecksumGenerated = TRUE;
}
}
//...Create target file
if(Info.pszTarget){
if((Info.hFile=CreateFile(Info.pszTarget, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
if (g_bVerbose)
{
printf("\nERROR: Error in creating target module [%s]\n", Info.pszSource);
}
dwError=GetLastError();
dwOffset=ERROR_OFFSET;
goto Error_Exit;
}
}
else{
if (g_bVerbose)
{
printf("\nERROR: There is no target file name.");
}
Usage();
dwError=ERROR_NO_TARGET;
dwOffset=0;
goto Error_Exit;
}
if(Info.wLanguage==0){
if (g_bVerbose)
{
printf("\nERROR: Can not find specified language name in the source module.");
}
Usage();
dwError=ERROR_NO_LANGUAGE_SPECIFIED;
dwOffset=0;
goto Error_Exit;
}
bInsertHeader(Info.hFile);
bEnumTypesReturn=EnumResourceTypes(hModule, EnumTypesFunc, (LONG_PTR)&Info);
//...Check for muibld errors
if(!Info.bContainsResources){
if (g_bVerbose)
{
printf("\nERROR: There is no resource in the source module.");
}
dwError=ERROR_NO_RESOURCES;
dwOffset=0;
goto Error_Exit;
}
if(!Info.bLanguageFound){
if (g_bVerbose)
{
printf("\nERROR: Resource in the specified language is not found in the source module.");
}
dwError=ERROR_LANGUAGE_NOT_IN_SOURCE;
dwOffset=0;
goto Error_Exit;
}
if(Info.bContainsOnlyVersion){
if (g_bVerbose)
{
printf("\nERROR: The source module only contains version information.");
}
dwError=ERROR_ONLY_VERSION_STAMP;
dwOffset=0;
goto Error_Exit;
}
//...Check for system errors in EnumResourceTypes
if(bEnumTypesReturn);
else{
dwError=GetLastError();
dwOffset=ERROR_OFFSET;
goto Error_Exit;
}
//...Check to see if extra resources were included
if(Info.bIncDependent){
CleanUp(&Info, hModule, FALSE);
return DEPENDENT_RESOURCE_REMOVED;
}
CleanUp(&Info, hModule, FALSE);
if (g_bVerbose)
{
printf("Resource file [%s] has been generated successfully.\n", Info.pszTarget);
}
return 0;
Error_Exit:
CleanUp(&Info, hModule, TRUE);
if(dwOffset==ERROR_OFFSET){
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, (LPTSTR)pszBuffer, 399, NULL);
fprintf(stderr, "\n%s\n", pszBuffer );
}
return dwError+dwOffset;
}
BOOL ParseCommandLine(int argc, char **argv, pCommandLineInfo pInfo){
int iCount=1, chOpt=0, iLast=argc;
int i;
int iNumInc;
BOOL bInc1=FALSE, bInc3=FALSE, bInc12=FALSE, bInc14=FALSE;
HRESULT hr;
HANDLE hFile;
iLast=argc;
//...Must have at least: muibld -l langid source
if(argc>3){
//...Determine the target and source files.
hFile = CreateFile(argv[argc-2], 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile !=INVALID_HANDLE_VALUE){
CloseHandle(hFile);
pInfo->pszSource=LocalAlloc(0, (strlen(argv[argc-2])+1) * sizeof(char));
if (pInfo->pszSource == NULL)
{
ExitFromOutOfMemory();
}
// strcpy(pInfo->pszSource, argv[argc-2]);
hr = StringCchCopyA(pInfo->pszSource, (strlen(argv[argc-2])+1) * sizeof(char),argv[argc-2]);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
pInfo->pszTarget=LocalAlloc(0, (strlen(argv[argc-1])+1) * sizeof(char));
if (pInfo->pszTarget == NULL)
{
ExitFromOutOfMemory();
}
// strcpy(pInfo->pszTarget, argv[argc-1]);
hr = StringCchCopyA(pInfo->pszTarget, (strlen(argv[argc-1])+1) * sizeof(char), argv[argc-1]);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
iLast=argc-2;
}
else {
pInfo->pszSource=LocalAlloc(0, (strlen(argv[argc-1])+1) * sizeof(char));
if (pInfo->pszSource == NULL)
{
ExitFromOutOfMemory();
}
//strcpy(pInfo->pszSource, argv[argc-1]);
hr = StringCchCopyA(pInfo->pszSource, (strlen(argv[argc-1])+1) * sizeof(char), argv[argc-1]);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
pInfo->pszTarget=LocalAlloc(0, (strlen(argv[argc-1]) + strlen(ADDED_EXT) + 1) * sizeof(char));
if (pInfo->pszTarget == NULL)
{
ExitFromOutOfMemory();
}
//strcpy(pInfo->pszTarget, strcat(argv[argc-1], ADDED_EXT));
hr = StringCchCatA(argv[argc-1], strlen(argv[argc-1]) + sizeof ADDED_EXT + 1, ADDED_EXT);
hr = StringCchCopyA(pInfo->pszTarget, (strlen(argv[argc-1]) + strlen(ADDED_EXT) + 1) * sizeof(char),
argv[argc-1]);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
iLast=argc-1;
}
}
//...Read in flags and arguments
while ( (iCount < iLast) && (*argv[iCount] == '-' || *argv[iCount] == '/')){
switch( ( chOpt = *CharLowerA( &argv[iCount][1]))) {
case '?':
case 'h':
printf("\n\n");
printf("MUIBLD [-h|?] [-v] [-c checksum_file] -l langid [-i resource_type] source_filename\n");
printf(" [target_filename]\n\n");
printf("-h(elp) or -?: Show help screen.\n\n");
printf("-i(nclude) Use to include certain resource types,\n");
printf("resource_type: e.g. -i 2 to include bitmaps.\n");
printf(" Multiple inclusion is possible. If this\n");
printf(" flag is not used, all types are included\n");
printf(" Standard resource types must be specified\n");
printf(" by number. See below for list.\n");
printf(" Types 1 and 12 are always included in pairs,\n");
printf(" even if only one is specified. Types 3 and 14\n");
printf(" are always included in pairs, too.\n\n");
printf("-v(erbose): Display source filename and target filename.\n\n");
printf("-l(anguage) langid: Extract only resource in this language.\n");
printf(" The language resource must be specified. The value is in decimal.\n\n");
printf("source_filename: The localized source file (no wildcard support)\n\n");
printf("target_filename: Optional. If no target_filename is specified,\n");
printf(" a second extension.RES is added to the\n");
printf(" source_filename.\n\n");
printf("Standard Resource Types: CURSOR(1) BITMAP(2) ICON(3) MENU(4) DIALOG(5)\n");
printf("STRING(6) FONTDIR(7) FONT(8) ACCELERATORS(9) RCDATA(10) MESSAGETABLE(11)\n");
printf("GROUP_CURSOR(12) GROUP_ICON(14) VERSION(16)\n");
iCount++;
break;
case 'v':
g_bVerbose=TRUE;
iCount++;
break;
case 'c':
iCount++;
pInfo->pszChecksumFile=LocalAlloc(0, (strlen(argv[iCount])+1) * sizeof(char));
if (pInfo->pszChecksumFile == NULL)
{
ExitFromOutOfMemory();
}
//strcpy(pInfo->pszChecksumFile, argv[iCount]);
hr = StringCchCopyA(pInfo->pszChecksumFile, (strlen(argv[iCount])+1) * sizeof(char),
argv[iCount]);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
iCount++;
break;
case 'b':
iCount++;
wChecksumLangId = (WORD)strtoul(argv[iCount], NULL, 0);
iCount++;
break;
case 'i':
if(argc<4)
return FALSE;
pInfo->bIncludeFlag=TRUE;
iNumInc=++iCount;
//...Allocate memory for and copy included types
while (argv[iNumInc][0]!='-' && iNumInc<iLast){
iNumInc++;
}
iNumInc-=iCount;
//... Allocate enough memory for specified included resources
// and unspecified resources dependent on them
pInfo->pszIncResType=LocalAlloc(0 ,(iNumInc+3)*sizeof(char *));
if (pInfo->pszIncResType == NULL)
{
ExitFromOutOfMemory();
}
i=0;
while(i<iNumInc){
pInfo->pszIncResType[i]=LocalAlloc(0, (strlen(argv[iCount])+1) * sizeof(char));
if (pInfo->pszIncResType[i] == NULL)
{
ExitFromOutOfMemory();
}
//strcpy(pInfo->pszIncResType[i], argv[iCount]);
hr = StringCchCopyA(pInfo->pszIncResType[i], (strlen(argv[iCount])+1) * sizeof(char),
argv[iCount]);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
switch(atoi(argv[iCount])){
case 1:
bInc1=TRUE;
break;
case 3:
bInc3=TRUE;
break;
case 12:
bInc12=TRUE;
break;
case 14:
bInc14=TRUE;
break;
default:
break;
}
i++;
iCount++;
}
//...If 1 or 12 is included, make sure both are included
if(bInc1 ^ bInc12){
pInfo->bIncDependent=TRUE;
if(bInc1){
pInfo->pszIncResType[i]=LocalAlloc(0, 3 * sizeof(char));
if (pInfo->pszIncResType[i] == NULL)
{
ExitFromOutOfMemory();
}
// strcpy(pInfo->pszIncResType[i], "12");
hr = StringCchCopyA(pInfo->pszIncResType[i], 3 * sizeof(char),"12");
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
i++;
}
else{
pInfo->pszIncResType[i]=LocalAlloc(0, 2 * sizeof(char));
if (pInfo->pszIncResType[i] == NULL)
{
ExitFromOutOfMemory();
}
// strcpy(pInfo->pszIncResType[i], "1");
hr = StringCchCopyA(pInfo->pszIncResType[i],2 * sizeof(char),"1");
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
i++;
}
}
//..If 3 or 14 is included, make sure both are included
if(bInc3 ^ bInc14){
pInfo->bIncDependent=TRUE;
if(bInc3){
pInfo->pszIncResType[i]=LocalAlloc(0, 3 * sizeof(char));
if (pInfo->pszIncResType[i] == NULL)
{
ExitFromOutOfMemory();
}
// strcpy(pInfo->pszIncResType[i], "14");
hr = StringCchCopyA(pInfo->pszIncResType[i],3 * sizeof(char),"14");
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
i++;
}
else{
pInfo->pszIncResType[i]=LocalAlloc(0, 2 * sizeof(char));
if (pInfo->pszIncResType[i] == NULL)
{
ExitFromOutOfMemory();
}
// strcpy(pInfo->pszIncResType[i], "3");
hr = StringCchCopyA(pInfo->pszIncResType[i],2 * sizeof(char),"3");
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
i++;
}
}
while(i<iNumInc + 3){
pInfo->pszIncResType[i++]=NULL;
}
break;
case 'l':
if(argc<4)
return FALSE;
iCount++;
pInfo->wLanguage=(WORD)strtol(argv[iCount], NULL, 0);
iCount++;
break;
}
}
if(argc<4)
return FALSE;
else
return TRUE;
}
BOOL CALLBACK EnumTypesFunc(HMODULE hModule, LPTSTR lpType, LONG_PTR lParam){
pCommandLineInfo pInfo;
pInfo=(pCommandLineInfo)lParam;
if(!pInfo->bIncludeFlag || bTypeIncluded((char *)lpType, pInfo->pszIncResType)) {
pInfo->bContainsResources=TRUE;
//...If the type is a string or a number other than 16...
if( (PtrToUlong(lpType) & 0xFFFF0000) || ((WORD)PtrToUlong(lpType)!=16) ){
pInfo->bContainsOnlyVersion=FALSE;
}
if(EnumResourceNames(hModule, (LPCTSTR)lpType, EnumNamesFunc, (LONG_PTR)pInfo));
else {
return FALSE;
}
}
return TRUE;
}
// This is a Var struct within VarFileInfo for storing checksum for the source file.
// The current size for this structure is 56 bytes.
typedef struct VAR_SRC_CHECKSUM
{
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szResourceChecksum[17]; // For storing "ResourceChecksum" null-terminated string in Unicode.
DWORD dwChecksum[4]; // 128 bit checksum = 16 bytes = 4 DWORD.
} VarResourceChecksum;
// This is a Var struct within VarFileInfo for stroing the resource types used in this file.
struct VarResourceTypes
{
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szResourceType[13];
//BYTE padding[0]; // WORD * 3 + UnicodeChar*13 = 32 bytes. So we need 0 bytes padding for DWORD alignment.
DWORD* dwTypes; // 128 bit checksum = 16 bytes = 4 DWORD.
};
BOOL WriteResHeader(
HANDLE hFile, LONG ResSize, LPCSTR lpType, LPCSTR lpName, WORD wLanguage, DWORD* pdwBytesWritten, DWORD* pdwHeaderSize)
{
DWORD iPadding;
WORD IdFlag=0xFFFF;
unsigned i;
LONG dwOffset;
//...write the resource's size.
PutDWord(hFile, ResSize, pdwBytesWritten, pdwHeaderSize);
//...Put in bogus header size
PutDWord(hFile, 0, pdwBytesWritten, pdwHeaderSize);
//...Write Resource Type
if(PtrToUlong(lpType) & 0xFFFF0000)
{
PutString(hFile, lpType, pdwBytesWritten, pdwHeaderSize);
}
else
{
PutWord(hFile, IdFlag, pdwBytesWritten, pdwHeaderSize);
PutWord(hFile, (USHORT)lpType, pdwBytesWritten, pdwHeaderSize);
}
//...Write Resource Name
if(PtrToUlong(lpName) & 0xFFFF0000){
PutString(hFile, lpName, pdwBytesWritten, pdwHeaderSize);
}
else{
PutWord(hFile, IdFlag, pdwBytesWritten, pdwHeaderSize);
PutWord(hFile, (USHORT)lpName, pdwBytesWritten, pdwHeaderSize);
}
//...Make sure Type and Name are DWORD-aligned
iPadding=(*pdwHeaderSize)%(sizeof(DWORD));
if(iPadding){
for(i=0; i<(sizeof(DWORD)-iPadding); i++){
PutByte (hFile, 0, pdwBytesWritten, pdwHeaderSize);
}
}
//...More Win32 header stuff
PutDWord(hFile, 0, pdwBytesWritten, pdwHeaderSize);
PutWord(hFile, 0x1030, pdwBytesWritten, pdwHeaderSize);
//...Write Language
PutWord(hFile, wLanguage, pdwBytesWritten, pdwHeaderSize);
//...More Win32 header stuff
PutDWord(hFile, 0, pdwBytesWritten, pdwHeaderSize); //... Version
PutDWord(hFile, 0, pdwBytesWritten, pdwHeaderSize); //... Characteristics
dwOffset=(*pdwHeaderSize)-4;
//...Set file pointer to where the header size is
if(SetFilePointer(hFile, -dwOffset, NULL, FILE_CURRENT));
else{
return FALSE;
}
PutDWord(hFile, (*pdwHeaderSize), pdwBytesWritten, NULL);
//...Set file pointer back to the end of the header
if(SetFilePointer(hFile, dwOffset-4, NULL, FILE_CURRENT));
else {
return FALSE;
}
return (TRUE);
}
BOOL WriteResource(HANDLE hFile, HMODULE hModule, WORD wLanguage, LPCSTR lpName, LPCSTR lpType, HRSRC hRsrc)
{
HGLOBAL hRes;
PVOID pv;
LONG ResSize=0L;
DWORD iPadding;
unsigned i;
DWORD dwBytesWritten;
DWORD dwHeaderSize=0L;
// Handle other types other than VS_VERSION_INFO
//...write the resource header
if(!(ResSize=SizeofResource(hModule, hRsrc)))
{
return FALSE;
}
//
// Generate an item in the RES format (*.res) file.
//
//
// First, we generated header for this resource.
//
if (!WriteResHeader(hFile, ResSize, lpType, lpName, wLanguage, &dwBytesWritten, &dwHeaderSize))
{
return (FALSE);
}
//Second, we copy resource data to the .res file
if (!(hRes=LoadResource(hModule, hRsrc)))
{
return FALSE;
}
if(!(pv=LockResource(hRes)))
{
return FALSE;
}
if (!WriteFile(hFile, pv, ResSize, &dwBytesWritten, NULL))
{
return FALSE;
}
//...Make sure resource is DWORD aligned
iPadding=dwBytesWritten%(sizeof(DWORD));
if(iPadding){
for(i=0; i<(sizeof(DWORD)-iPadding); i++){
PutByte (hFile, 0, &dwBytesWritten, NULL);
}
}
return TRUE;
}
LPBYTE UpdateAddr(LPBYTE pAddr, WORD size, WORD* len)
{
*len += size;
return (pAddr + size);
}
LPBYTE AddPadding(LPBYTE pAddr, WORD* padding, WORD* len)
{
if ((*padding = *len % 4) != 0)
{
*padding = (4 - *padding);
*len += *padding;
return (pAddr + *padding);
}
return (pAddr);
}
BOOL WriteVersionResource(
HANDLE hFile, HMODULE hModule, WORD wLanguage, LPCSTR lpName, LPCSTR lpType, HRSRC hRsrc, unsigned char* pbChecksum)
{
LONG ResSize=0L, OldResSize=0L;
DWORD dwBytesWritten;
DWORD dwHeaderSize=0L;
WORD IdFlag=0xFFFF;
BYTE* newVersionData;
BYTE* pAddr;
VarResourceChecksum varResourceChecksum;
PVOID pv = NULL;
HGLOBAL hRes;
WORD len = 0;
WORD wLength;
WORD wValueLength;
WORD wType;
LPWSTR szKey;
WORD wPadding1Count;
LPBYTE pValue;
WORD wPadding2Count;
int wTotalLen;
BOOL isVS_VERSIONINFO = TRUE;
BOOL isVarFileInfo = FALSE;
BOOL isStringFileInfo = FALSE;
BOOL bRet = FALSE;
HRESULT hr;
//
// Copy resource data from the .res file
//
if (hRes=LoadResource(hModule, hRsrc))
{
pv=LockResource(hRes);
}
if (pv)
{
//
// The first WORD is the size of the VERSIONINFO resource.
//
OldResSize = *((WORD*)pv);
ResSize = OldResSize + sizeof(VarResourceChecksum);
//
// Generate an item in the RES format (*.res) file.
//
//
// First, we generated header for this resource in the RES file.
//
if (WriteResHeader(hFile, ResSize, lpType, lpName, wLanguage, &dwBytesWritten, &dwHeaderSize) &&
(newVersionData = (BYTE*)LocalAlloc(0, ResSize)))
{
bRet = TRUE;
memcpy(newVersionData, pv, OldResSize);
// Add the length of new VarResourceChecksum structure to VS_VERSIONINFO.wLength.
pAddr = newVersionData;
wTotalLen = *((WORD*)pAddr);
while (wTotalLen > 0)
{
len = 0;
wPadding1Count = 0;
wPadding2Count = 0;
// wLength
wLength = *((WORD*)pAddr);
pAddr = UpdateAddr(pAddr, sizeof(WORD), &len);
// wValueLength
wValueLength = *((WORD*)pAddr);
pAddr = UpdateAddr(pAddr, sizeof(WORD), &len);
// wType
wType = *((WORD*)pAddr);
pAddr = UpdateAddr(pAddr, sizeof(WORD), &len);
// szKey
szKey = (LPWSTR)pAddr;
pAddr = UpdateAddr(pAddr, (WORD)((wcslen((WCHAR*)pAddr) + 1) * sizeof(WCHAR)), &len);
// Padding 1
pAddr = AddPadding(pAddr, &wPadding1Count, &len);
// Value
pValue = pAddr;
if (wValueLength > 0)
{
if (wType==1)
{
// In the case of String, the wValueLength is in WORD (not in BYTE).
pAddr = UpdateAddr(pAddr, (WORD)(wValueLength * sizeof(WCHAR)), &len);
// Padding 2
pAddr = AddPadding(pAddr, &wPadding2Count, &len);
} else
{
pAddr = UpdateAddr(pAddr, wValueLength, &len);
if (isStringFileInfo)
{
//
// Generally, padding is not necessary in binary data.
// However, in some rare cases, people use binary data in the StringFileInfo (
// which is not really appropriate),
// So we need to add proper padding here to get around this.
//
// Padding 2
pAddr = AddPadding(pAddr, &wPadding2Count, &len);
}
}
}
if (isVS_VERSIONINFO)
{
//
// This is VS_VERSION_INFO.
//
// VS_VERSIONINFO can have padding 2.
isVS_VERSIONINFO = FALSE;
// Padding 2
pAddr = AddPadding(pAddr, &wPadding2Count, &len);
//
// Add the new VarResourceChecksum structure.
//
wLength += sizeof(VarResourceChecksum);
}
if (wcscmp(szKey, L"StringFileInfo") == 0)
{
isStringFileInfo = TRUE;
}
if (wcscmp(szKey, L"VarFileInfo") == 0)
{
isStringFileInfo = FALSE;
isVarFileInfo = TRUE;
wLength += sizeof(VarResourceChecksum);
}
PutWord(hFile, wLength, &dwBytesWritten, NULL);
PutWord(hFile, wValueLength, &dwBytesWritten, NULL);
PutWord(hFile, wType, &dwBytesWritten, NULL);
PutStringW(hFile, szKey, &dwBytesWritten, NULL);
PutPadding(hFile, wPadding1Count, &dwBytesWritten, NULL);
WriteFile(hFile, pValue, wValueLength * (wType == 0 ? sizeof(BYTE) : sizeof(WCHAR)), &dwBytesWritten, NULL);
PutPadding(hFile, wPadding2Count, &dwBytesWritten, NULL);
if (isVarFileInfo && wcscmp(szKey, L"Translation") == 0)
{
isVarFileInfo = FALSE;
varResourceChecksum.wLength = sizeof(VarResourceChecksum);
varResourceChecksum.wValueLength = 16; // 128 bits checksum = 16 bytes
varResourceChecksum.wType = 0;
// wcscpy(varResourceChecksum.szResourceChecksum, RESOURCE_CHECK_SUM);
hr = StringCchCopyW(varResourceChecksum.szResourceChecksum, sizeof (varResourceChecksum.szResourceChecksum)/ sizeof (WCHAR),
RESOURCE_CHECK_SUM);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
memcpy(varResourceChecksum.dwChecksum, pbChecksum, 16);
if (!WriteFile(hFile, &varResourceChecksum, sizeof(VarResourceChecksum), &dwBytesWritten, NULL))
{
bRet = FALSE;
break;
}
}
wTotalLen -= len;
}
LocalFree(newVersionData);
}
}
return (bRet);
}
BOOL CALLBACK EnumLangsFunc(
HANDLE hModule, // module handle
LPCTSTR lpType, // address of resource type
LPCTSTR lpName, // address of resource name
WORD wLang, // resource language
LONG_PTR lParam) // extra parameter, could be
// used for error checking
{
HANDLE hResInfo;
char szBuffer[80];
int cbString = 0;
pCommandLineInfo pInfo;
if (lParam == 0)
{
printf("EnumLangsFunc: lParam is invalid!\n");
return FALSE;
}
pInfo = (pCommandLineInfo) lParam;
//
// only write out the resource if the language id matches, otherwise just skip it and return true
// the call back will return whatever Write(Version)Resource function(s) return
//
//
// Multi lingual binary has more than two language ID, in this case
// we don't want to extract english version resource. (this is a fix of side effect; forcing english version
// saved as localized one)
//
if ((WORD)PtrToUlong(lpType) == 16 && wLang == 0x409 && pInfo->wLanguage != 0x409)
{
hResInfo = FindResourceEx(hModule, lpType, lpName, pInfo->wLanguage);
if (hResInfo)
{ // This is multi lingual DLL. we don't want to extract English Version resource.
return TRUE;
}
}
//
// Some files does not localize VERSIOIN so we force VERSION should be added into MUI file as localized one.
// if resource in the binary has more than two types, and type 16 is smallest resource number, then
// it does not work in this routine.
//
if (pInfo->wLanguage == wLang || ( (WORD)PtrToUlong(lpType) == 16 && wLang == 0x409 && pInfo->bLanguageFound ) )
{
pInfo->bLanguageFound=TRUE; // set this to true now that we have found at least one resource
hResInfo=FindResourceEx(hModule, lpType, lpName, pInfo->wLanguage);
if (hResInfo)
{
if (lpType == MAKEINTRESOURCE(RT_VERSION) && pInfo->bIsResChecksumGenerated)
{
//
// If this is a version resource and resource checksum is generated, call
// the following function to embed the resource checksum into the version
// resource.
//
return (WriteVersionResource(pInfo->hFile, hModule, pInfo->wLanguage, lpName, lpType, hResInfo, pInfo->pResourceChecksum));
}
return (WriteResource(pInfo->hFile, hModule, pInfo->wLanguage, lpName, lpType, hResInfo));
}
}
return TRUE;
}
BOOL CALLBACK EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG_PTR lParam)
{
HRSRC hRsrc;
pCommandLineInfo pInfo;
if(lParam == 0)
{
printf( "MUIBLD: EnumNamesFunc lParam value incorrect (%d)\n", lParam );
return FALSE;
}
return EnumResourceLanguages(hModule,
lpType,
lpName,
(ENUMRESLANGPROC)EnumLangsFunc,
lParam);
}
BOOL bTypeIncluded(LPCSTR lpType, char **pszIncResType){
char *pszBuf;
char **p;
HRESULT hr;
if (PtrToUlong(lpType) & 0xFFFF0000) {
pszBuf=LocalAlloc(0, strlen(lpType) +1);
if (pszBuf == NULL)
{
ExitFromOutOfMemory();
}
//sprintf(pszBuf, "%s", lpType);
hr = StringCbPrintfA(pszBuf, strlen(lpType) +1, "%s", lpType);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
}
else {
WORD wType = (WORD) lpType;
pszBuf=LocalAlloc(0, sizeof(lpType) + 1);
if (pszBuf == NULL)
{
ExitFromOutOfMemory();
}
// sprintf(pszBuf, "%u", wType);
hr = StringCbPrintfA(pszBuf, sizeof(lpType) +1, "%u", wType);
if ( ! SUCCEEDED(hr)){
ExitFromSafeStringError();
}
}
p=pszIncResType;
while(p && *p){
if(strcmp(pszBuf, *p)==0)
return TRUE;
p++;
}
LocalFree(pszBuf);
return FALSE;
}
BOOL bInsertHeader(HANDLE hFile){
DWORD dwBytesWritten;
PutByte (hFile, 0x00, &dwBytesWritten, NULL);
PutByte (hFile, 0x00, &dwBytesWritten, NULL);
PutByte (hFile, 0x00, &dwBytesWritten, NULL);
PutByte (hFile, 0x00, &dwBytesWritten, NULL);
PutByte (hFile, 0x20, &dwBytesWritten, NULL);
PutByte (hFile, 0x00, &dwBytesWritten, NULL);
PutByte (hFile, 0x00, &dwBytesWritten, NULL);
PutByte (hFile, 0x00, &dwBytesWritten, NULL);
PutWord (hFile, 0xffff, &dwBytesWritten, NULL);
PutWord (hFile, 0x00, &dwBytesWritten, NULL);
PutWord (hFile, 0xffff, &dwBytesWritten, NULL);
PutWord (hFile, 0x00, &dwBytesWritten, NULL);
PutDWord (hFile, 0L, &dwBytesWritten, NULL);
PutDWord (hFile, 0L, &dwBytesWritten, NULL);
PutDWord (hFile, 0L, &dwBytesWritten, NULL);
PutDWord (hFile, 0L, &dwBytesWritten, NULL);
return TRUE;
}
void PutByte(HANDLE OutFile, TCHAR b, LONG *plSize1, LONG *plSize2){
BYTE temp=b;
if (plSize2){
(*plSize2)++;
}
WriteFile(OutFile, &b, 1, plSize1, NULL);
}
void PutWord(HANDLE OutFile, WORD w, LONG *plSize1, LONG *plSize2){
PutByte(OutFile, (BYTE) LOBYTE(w), plSize1, plSize2);
PutByte(OutFile, (BYTE) HIBYTE(w), plSize1, plSize2);
}
void PutDWord(HANDLE OutFile, DWORD l, LONG *plSize1, LONG *plSize2){
PutWord(OutFile, LOWORD(l), plSize1, plSize2);
PutWord(OutFile, HIWORD(l), plSize1, plSize2);
}
void PutString(HANDLE OutFile, LPCSTR szStr , LONG *plSize1, LONG *plSize2){
WORD i = 0;
do {
PutWord( OutFile , szStr[ i ], plSize1, plSize2);
}
while ( szStr[ i++ ] != TEXT('\0') );
}
void PutStringW(HANDLE OutFile, LPCWSTR szStr , LONG *plSize1, LONG *plSize2){
WORD i = 0;
do {
PutWord( OutFile , szStr[ i ], plSize1, plSize2);
}
while ( szStr[ i++ ] != L'\0' );
}
void PutPadding(HANDLE OutFile, int paddingCount, LONG *plSize1, LONG *plSize2)
{
int i;
for (i = 0; i < paddingCount; i++)
{
PutByte(OutFile, 0x00, plSize1, plSize2);
}
}
void Usage(){
printf("MUIBLD [-h|?] [-c checksum_filename] [-v] -l langid [-i resource_type] source_filename\n");
printf(" [target_filename]\n\n");
}
void CleanUp(pCommandLineInfo pInfo, HANDLE hModule, BOOL bDeleteFile){
if(hModule)
FreeLibrary(hModule);
if(pInfo->hFile)
CloseHandle(pInfo->hFile);
if(bDeleteFile && pInfo->pszTarget)
DeleteFile(pInfo->pszTarget);
}
void FreeAll(pCommandLineInfo pInfo){
char **p;
LocalFree(pInfo->pszSource);
LocalFree(pInfo->pszTarget);
if(pInfo->pszIncResType){
p=pInfo->pszIncResType;
while(p && *p){
LocalFree(*p);
p++;
}
LocalFree(pInfo->pszIncResType);
}
}
void ExitFromOutOfMemory()
{
printf("Out of memory. Can not continue. GetLastError() = 0x%x.", GetLastError());
exit(1);
}
void ExitFromSafeStringError()
{
printf("Safe string return error. Can not continue.\n");
exit(1);
}