#include #include #include #include #include #include #include #include // 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]!='-' && iNumIncpszIncResType=LocalAlloc(0 ,(iNumInc+3)*sizeof(char *)); if (pInfo->pszIncResType == NULL) { ExitFromOutOfMemory(); } i=0; while(ipszIncResType[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(ipszIncResType[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); }