| /*  | 
 |  * Implementation of VER.DLL | 
 |  *  | 
 |  * Copyright 1996,1997 Marcus Meissner | 
 |  * Copyright 1997 David Cuthbert | 
 |  */ | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <ctype.h> | 
 | #include <unistd.h> | 
 | #include "windows.h" | 
 | #include "win.h" | 
 | #include "winerror.h" | 
 | #include "heap.h" | 
 | #include "ver.h" | 
 | #include "lzexpand.h" | 
 | #include "module.h" | 
 | #include "neexe.h" | 
 | #include "debug.h" | 
 | #include "xmalloc.h" | 
 | #include "winreg.h" | 
 |  | 
 | #define LZREAD(what) \ | 
 |   if (sizeof(*what)!=LZRead32(lzfd,what,sizeof(*what))) return 0; | 
 | #define LZTELL(lzfd) LZSeek32(lzfd, 0, SEEK_CUR); | 
 |  | 
 | /****************************************************************************** | 
 |  * | 
 |  *   void  ver_dstring( | 
 |  *      char const * prologue, | 
 |  *      char const * teststring, | 
 |  *      char const * epilogue ) | 
 |  * | 
 |  *   This function will print via dprintf[_]ver to stddeb the prologue string, | 
 |  *   followed by the address of teststring and the string it contains if | 
 |  *   teststring is non-null or "(null)" otherwise, and then the epilogue | 
 |  *   string followed by a new line. | 
 |  * | 
 |  *   Revision history | 
 |  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) | 
 |  *         Original implementation as dprintf[_]ver_string | 
 |  *      05-Jul-1997 Dave Cuthbert (dacut@ece.cmu.edu) | 
 |  *         Fixed problem that caused bug with tools/make_debug -- renaming | 
 |  *         this function should fix the problem. | 
 |  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu) | 
 |  *         Modified it to make it print the message using only one | 
 |  *         dprintf[_]ver call. | 
 |  * | 
 |  *****************************************************************************/ | 
 |  | 
 | static void  ver_dstring( | 
 |     char const * prologue, | 
 |     char const * teststring, | 
 |     char const * epilogue ) | 
 | { | 
 |     TRACE(ver, "%s %p (\"%s\") %s\n", prologue,  | 
 | 		(void const *) teststring,  | 
 | 		teststring ? teststring : "(null)", | 
 | 		epilogue); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * | 
 |  *   This function will print via dprintf[_]ver to stddeb debug info regarding | 
 |  *   the file info structure vffi. | 
 |  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu) | 
 |  *      Added this function to clean up the code. | 
 |  * | 
 |  *****************************************************************************/ | 
 | static void print_vffi_debug(VS_FIXEDFILEINFO *vffi) | 
 | { | 
 |         dbg_decl_str(ver, 1024); | 
 |  | 
 | 	TRACE(ver," structversion=%u.%u, fileversion=%u.%u.%u.%u, productversion=%u.%u.%u.%u, flagmask=0x%lx, flags=%s%s%s%s%s%s\n", | 
 | 		    HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion), | 
 | 		    HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS), | 
 | 		    HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS), | 
 | 		    HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS), | 
 | 		    HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS), | 
 | 		    vffi->dwFileFlagsMask, | 
 | 		    (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "", | 
 | 		    (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "", | 
 | 		    (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "", | 
 | 		    (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "", | 
 | 		    (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "", | 
 | 		    (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "" | 
 | 		    ); | 
 |  | 
 | 	dsprintf(ver," OS=0x%x.0x%x ", | 
 | 		HIWORD(vffi->dwFileOS), | 
 | 		LOWORD(vffi->dwFileOS) | 
 | 	); | 
 | 	switch (vffi->dwFileOS&0xFFFF0000) { | 
 | 	case VOS_DOS:dsprintf(ver,"DOS,");break; | 
 | 	case VOS_OS216:dsprintf(ver,"OS/2-16,");break; | 
 | 	case VOS_OS232:dsprintf(ver,"OS/2-32,");break; | 
 | 	case VOS_NT:dsprintf(ver,"NT,");break; | 
 | 	case VOS_UNKNOWN: | 
 | 	default: | 
 | 		dsprintf(ver,"UNKNOWN(0x%lx),",vffi->dwFileOS&0xFFFF0000);break; | 
 | 	} | 
 | 	switch (LOWORD(vffi->dwFileOS)) { | 
 | 	case VOS__BASE:dsprintf(ver,"BASE");break; | 
 | 	case VOS__WINDOWS16:dsprintf(ver,"WIN16");break; | 
 | 	case VOS__WINDOWS32:dsprintf(ver,"WIN32");break; | 
 | 	case VOS__PM16:dsprintf(ver,"PM16");break; | 
 | 	case VOS__PM32:dsprintf(ver,"PM32");break; | 
 | 	default:dsprintf(ver,"UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break; | 
 | 	} | 
 | 	TRACE(ver, "(%s)\n", dbg_str(ver)); | 
 |  | 
 | 	dbg_reset_str(ver); | 
 | 	switch (vffi->dwFileType) { | 
 | 	default: | 
 | 	case VFT_UNKNOWN: | 
 | 		dsprintf(ver,"filetype=Unknown(0x%lx)",vffi->dwFileType); | 
 | 		break; | 
 | 	case VFT_APP:dsprintf(ver,"filetype=APP,");break; | 
 | 	case VFT_DLL:dsprintf(ver,"filetype=DLL,");break; | 
 | 	case VFT_DRV: | 
 | 		dsprintf(ver,"filetype=DRV,"); | 
 | 		switch(vffi->dwFileSubtype) { | 
 | 		default: | 
 | 		case VFT2_UNKNOWN: | 
 | 			dsprintf(ver,"UNKNOWN(0x%lx)",vffi->dwFileSubtype); | 
 | 			break; | 
 | 		case VFT2_DRV_PRINTER: | 
 | 			dsprintf(ver,"PRINTER"); | 
 | 			break; | 
 | 		case VFT2_DRV_KEYBOARD: | 
 | 			dsprintf(ver,"KEYBOARD"); | 
 | 			break; | 
 | 		case VFT2_DRV_LANGUAGE: | 
 | 			dsprintf(ver,"LANGUAGE"); | 
 | 			break; | 
 | 		case VFT2_DRV_DISPLAY: | 
 | 			dsprintf(ver,"DISPLAY"); | 
 | 			break; | 
 | 		case VFT2_DRV_MOUSE: | 
 | 			dsprintf(ver,"MOUSE"); | 
 | 			break; | 
 | 		case VFT2_DRV_NETWORK: | 
 | 			dsprintf(ver,"NETWORK"); | 
 | 			break; | 
 | 		case VFT2_DRV_SYSTEM: | 
 | 			dsprintf(ver,"SYSTEM"); | 
 | 			break; | 
 | 		case VFT2_DRV_INSTALLABLE: | 
 | 			dsprintf(ver,"INSTALLABLE"); | 
 | 			break; | 
 | 		case VFT2_DRV_SOUND: | 
 | 			dsprintf(ver,"SOUND"); | 
 | 			break; | 
 | 		case VFT2_DRV_COMM: | 
 | 			dsprintf(ver,"COMM"); | 
 | 			break; | 
 | 		case VFT2_DRV_INPUTMETHOD: | 
 | 			dsprintf(ver,"INPUTMETHOD"); | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	case VFT_FONT: | 
 | 		dsprintf(ver,"filetype=FONT."); | 
 | 		switch (vffi->dwFileSubtype) { | 
 | 		default: | 
 | 			dsprintf(ver,"UNKNOWN(0x%lx)",vffi->dwFileSubtype); | 
 | 			break; | 
 | 		case VFT2_FONT_RASTER:dsprintf(ver,"RASTER");break; | 
 | 		case VFT2_FONT_VECTOR:dsprintf(ver,"VECTOR");break; | 
 | 		case VFT2_FONT_TRUETYPE:dsprintf(ver,"TRUETYPE");break; | 
 | 		} | 
 | 		break; | 
 | 	case VFT_VXD:dsprintf(ver,"filetype=VXD");break; | 
 | 	case VFT_STATIC_LIB:dsprintf(ver,"filetype=STATIC_LIB");break; | 
 | 	} | 
 | 	TRACE(ver, "%s\n", dbg_str(ver)); | 
 |  | 
 | 	TRACE(ver, "  filedata=0x%lx.0x%lx\n", | 
 | 		    vffi->dwFileDateMS,vffi->dwFileDateLS); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * | 
 |  *   int  testFileExistence( | 
 |  *      char const * path, | 
 |  *      char const * file ) | 
 |  * | 
 |  *   Tests whether a given path/file combination exists.  If the file does | 
 |  *   not exist, the return value is zero.  If it does exist, the return | 
 |  *   value is non-zero. | 
 |  * | 
 |  *   Revision history | 
 |  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) | 
 |  *         Original implementation | 
 |  * | 
 |  *****************************************************************************/ | 
 |  | 
 | static int  testFileExistence( | 
 |    char const * path, | 
 |    char const * file ) | 
 | { | 
 |     char  filename[1024]; | 
 |     int  filenamelen; | 
 |     OFSTRUCT  fileinfo; | 
 |     int  retval; | 
 |  | 
 |     fileinfo.cBytes = sizeof(OFSTRUCT); | 
 |  | 
 |     strcpy(filename, path); | 
 |     filenamelen = strlen(filename); | 
 |  | 
 |     /* Add a trailing \ if necessary */ | 
 |     if(filenamelen) { | 
 | 	if(filename[filenamelen - 1] != '\\') | 
 | 	    strcat(filename, "\\"); | 
 |     } | 
 |     else /* specify the current directory */ | 
 | 	strcpy(filename, ".\\"); | 
 |  | 
 |     /* Create the full pathname */ | 
 |     strcat(filename, file); | 
 |  | 
 |     if(OpenFile32(filename, &fileinfo, OF_EXIST) == HFILE_ERROR32) | 
 | 	retval = 0; | 
 |     else | 
 | 	retval = 1; | 
 |  | 
 |     return  retval; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * | 
 |  *   int  testFileExclusiveExistence( | 
 |  *      char const * path, | 
 |  *      char const * file ) | 
 |  * | 
 |  *   Tests whether a given path/file combination exists and ensures that no | 
 |  *   other programs have handles to the given file.  If the file does not | 
 |  *   exist or is open, the return value is zero.  If it does exist, the | 
 |  *   return value is non-zero. | 
 |  * | 
 |  *   Revision history | 
 |  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) | 
 |  *         Original implementation | 
 |  * | 
 |  *****************************************************************************/ | 
 |  | 
 | static int  testFileExclusiveExistence( | 
 |    char const * path, | 
 |    char const * file ) | 
 | { | 
 |     char  filename[1024]; | 
 |     int  filenamelen; | 
 |     OFSTRUCT  fileinfo; | 
 |     int  retval; | 
 |  | 
 |     fileinfo.cBytes = sizeof(OFSTRUCT); | 
 |  | 
 |     strcpy(filename, path); | 
 |     filenamelen = strlen(filename); | 
 |  | 
 |     /* Add a trailing \ if necessary */ | 
 |     if(filenamelen) { | 
 | 	if(filename[filenamelen - 1] != '\\') | 
 | 	    strcat(filename, "\\"); | 
 |     } | 
 |     else /* specify the current directory */ | 
 | 	strcpy(filename, ".\\"); | 
 |  | 
 |     /* Create the full pathname */ | 
 |     strcat(filename, file); | 
 |  | 
 |     if(OpenFile32(filename, &fileinfo, OF_EXIST | OF_SHARE_EXCLUSIVE) == | 
 |        HFILE_ERROR32) | 
 | 	retval = 0; | 
 |     else | 
 | 	retval = 1; | 
 |  | 
 |     return retval; | 
 | } | 
 |  | 
 |  | 
 | static int read_xx_header(HFILE32 lzfd) { | 
 | 	IMAGE_DOS_HEADER	mzh; | 
 | 	char			magic[3]; | 
 |  | 
 | 	LZSeek32(lzfd,0,SEEK_SET); | 
 | 	if (sizeof(mzh)!=LZRead32(lzfd,&mzh,sizeof(mzh))) | 
 | 		return 0; | 
 | 	if (mzh.e_magic!=IMAGE_DOS_SIGNATURE) | 
 | 		return 0; | 
 | 	LZSeek32(lzfd,mzh.e_lfanew,SEEK_SET); | 
 | 	if (2!=LZRead32(lzfd,magic,2)) | 
 | 		return 0; | 
 | 	LZSeek32(lzfd,mzh.e_lfanew,SEEK_SET); | 
 | 	if (magic[0] == 'N' && magic[1] == 'E') | 
 | 		return IMAGE_OS2_SIGNATURE; | 
 | 	if (magic[0] == 'P' && magic[1] == 'E') | 
 | 		return IMAGE_NT_SIGNATURE; | 
 | 	magic[2]='\0'; | 
 | 	WARN(ver,"Can't handle %s files.\n",magic); | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | static int find_ne_resource( | 
 | 	HFILE32 lzfd,SEGPTR typeid,SEGPTR resid, | 
 | 	BYTE **resdata,int *reslen,DWORD *off | 
 | ) { | 
 | 	IMAGE_OS2_HEADER nehd; | 
 | 	NE_TYPEINFO	ti; | 
 | 	NE_NAMEINFO	ni; | 
 | 	int		i; | 
 | 	WORD		shiftcount; | 
 | 	DWORD		nehdoffset; | 
 |  | 
 | 	nehdoffset = LZTELL(lzfd); | 
 | 	LZREAD(&nehd); | 
 | 	if (nehd.resource_tab_offset==nehd.rname_tab_offset) { | 
 | 		TRACE(ver,"no resources in NE dll\n"); | 
 | 		return 0; | 
 | 	} | 
 | 	LZSeek32(lzfd,nehd.resource_tab_offset+nehdoffset,SEEK_SET); | 
 | 	LZREAD(&shiftcount); | 
 | 	TRACE(ver,"shiftcount is %d\n",shiftcount); | 
 | 	TRACE(ver,"reading resource typeinfo dir.\n"); | 
 |  | 
 | 	if (!HIWORD(typeid)) typeid = (SEGPTR)(LOWORD(typeid) | 0x8000); | 
 | 	if (!HIWORD(resid))  resid  = (SEGPTR)(LOWORD(resid) | 0x8000); | 
 | 	while (1) { | 
 | 		int	skipflag; | 
 |  | 
 | 		LZREAD(&ti); | 
 | 		if (!ti.type_id) | 
 | 			return 0; | 
 | 		TRACE(ver,"    ti.typeid =%04x,count=%d\n",ti.type_id,ti.count); | 
 |  | 
 | 		skipflag=0; | 
 | 		if (!HIWORD(typeid)) { | 
 | 			if ((ti.type_id&0x8000)&&(typeid!=ti.type_id)) | 
 | 				skipflag=1; | 
 | 		} else { | 
 | 			if (ti.type_id & 0x8000) { | 
 | 				skipflag=1;  | 
 | 			} else { | 
 | 				BYTE	len; | 
 | 				char	*str; | 
 | 				DWORD	whereleft; | 
 |  | 
 | 				whereleft = LZTELL(lzfd); | 
 | 				LZSeek32( | 
 | 					lzfd, | 
 | 					nehdoffset+nehd.resource_tab_offset+ti.type_id, | 
 | 					SEEK_SET | 
 | 				); | 
 | 				LZREAD(&len); | 
 | 				str=xmalloc(len); | 
 | 				if (len!=LZRead32(lzfd,str,len)) | 
 | 					return 0; | 
 | 				TRACE(ver,"read %s to compare it with %s\n", | 
 | 					str,(char*)PTR_SEG_TO_LIN(typeid) | 
 | 				); | 
 | 				if (lstrcmpi32A(str,(char*)PTR_SEG_TO_LIN(typeid))) | 
 | 					skipflag=1; | 
 | 				free(str); | 
 | 				LZSeek32(lzfd,whereleft,SEEK_SET); | 
 | 			} | 
 | 		} | 
 | 		if (skipflag) { | 
 | 			LZSeek32(lzfd,ti.count*sizeof(ni),SEEK_CUR); | 
 | 			continue; | 
 | 		} | 
 | 		for (i=0;i<ti.count;i++) { | 
 | 			WORD	*rdata; | 
 | 			int	len; | 
 |  | 
 | 			LZREAD(&ni); | 
 | 			TRACE(ver,"	ni.id=%4x,offset=%d,length=%d\n", | 
 | 				ni.id,ni.offset,ni.length | 
 | 			); | 
 | 			skipflag=1; | 
 | 			if (!HIWORD(resid)) { | 
 | 				if (ni.id == resid) | 
 | 					skipflag=0; | 
 | 			} else { | 
 | 				if (!(ni.id & 0x8000)) { | 
 | 					BYTE	len; | 
 | 					char	*str; | 
 | 					DWORD	whereleft; | 
 |  | 
 | 					whereleft = LZTELL(lzfd); | 
 | 					  LZSeek32( | 
 | 						lzfd, | 
 | 						nehdoffset+nehd.resource_tab_offset+ni.id, | 
 | 						SEEK_SET | 
 | 					); | 
 | 					LZREAD(&len); | 
 | 					str=xmalloc(len); | 
 | 					if (len!=LZRead32(lzfd,str,len)) | 
 | 						return 0; | 
 | 					TRACE(ver,"read %s to compare it with %s\n", | 
 | 						str,(char*)PTR_SEG_TO_LIN(typeid) | 
 | 					); | 
 | 					if (!lstrcmpi32A(str,(char*)PTR_SEG_TO_LIN(typeid))) | 
 | 						skipflag=0; | 
 | 					free(str); | 
 | 					LZSeek32(lzfd,whereleft,SEEK_SET); | 
 | 				} | 
 | 			} | 
 | 			if (skipflag) | 
 | 				continue; | 
 | 			LZSeek32(lzfd,((int)ni.offset<<shiftcount),SEEK_SET); | 
 | 			*off	= (int)ni.offset<<shiftcount; | 
 | 			len	= ni.length<<shiftcount; | 
 | 			rdata=(WORD*)xmalloc(len); | 
 | 			if (len!=LZRead32(lzfd,rdata,len)) { | 
 | 				free(rdata); | 
 | 				return 0; | 
 | 			} | 
 | 			TRACE(ver,"resource found.\n"); | 
 | 			*resdata= (BYTE*)rdata; | 
 | 			*reslen	= len; | 
 | 			return 1; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /* Loads the specified PE resource. | 
 |  * FIXME: shouldn't load the whole image | 
 |  */ | 
 | static int | 
 | find_pe_resource( | 
 | 	HFILE32 lzfd,LPWSTR typeid,LPWSTR resid, | 
 | 	BYTE **resdata,int *reslen,DWORD *off | 
 | ) { | 
 | 	IMAGE_NT_HEADERS pehd; | 
 | 	int		i; | 
 | 	UINT32		nrofsections; | 
 | 	DWORD		imagesize,pehdoffset; | 
 | 	BYTE		*image; | 
 | 	IMAGE_DATA_DIRECTORY		resdir; | 
 | 	PIMAGE_RESOURCE_DIRECTORY	resourcedir,xresdir; | 
 | 	PIMAGE_RESOURCE_DATA_ENTRY	xresdata; | 
 | 	PIMAGE_SECTION_HEADER		sections; | 
 |  | 
 | 	pehdoffset = LZTELL(lzfd); | 
 | 	LZREAD(&pehd); | 
 | 	resdir = pehd.OptionalHeader.DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY]; | 
 | 	TRACE(ver,"(.,type=%p, id=%p, len=%u, off=%lu)\n",typeid,resid,*reslen,*off); | 
 | 	if (!resdir.Size) { | 
 | 		WARN(ver,"No resource directory found in PE file.\n"); | 
 | 		return 0; | 
 | 	} | 
 | 	imagesize = pehd.OptionalHeader.SizeOfImage; | 
 | 	image = HeapAlloc(GetProcessHeap(),0,imagesize); | 
 | 	nrofsections = pehd.FileHeader.NumberOfSections; | 
 |  | 
 | 	sections = (PIMAGE_SECTION_HEADER)HeapAlloc(GetProcessHeap(),0,pehd.FileHeader.NumberOfSections*sizeof(IMAGE_SECTION_HEADER)); | 
 | 	LZSeek32(lzfd, | 
 | 		pehdoffset+ | 
 | 		sizeof(DWORD)+	/* Signature */ | 
 | 		sizeof(IMAGE_FILE_HEADER)+	 | 
 | 		pehd.FileHeader.SizeOfOptionalHeader, | 
 | 		SEEK_SET | 
 | 	); | 
 | 	if (	nrofsections*sizeof(IMAGE_SECTION_HEADER)!= | 
 | 		LZRead32(lzfd,sections,nrofsections*sizeof(IMAGE_SECTION_HEADER)) | 
 | 	) { | 
 | 		HeapFree(GetProcessHeap(),0,image); | 
 | 		return 0; | 
 | 	} | 
 | 	for (i=0;i<nrofsections;i++) { | 
 | 		if (sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) | 
 | 			continue; | 
 | 		LZSeek32(lzfd,sections[i].PointerToRawData,SEEK_SET); | 
 | 		if (	sections[i].SizeOfRawData!= | 
 | 			LZRead32(lzfd,image+sections[i].VirtualAddress,sections[i].SizeOfRawData) | 
 | 		) { | 
 | 			HeapFree(GetProcessHeap(),0,image); | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	resourcedir = (PIMAGE_RESOURCE_DIRECTORY)(image+resdir.VirtualAddress); | 
 | 	xresdir = GetResDirEntryW(resourcedir,typeid,(DWORD)resourcedir,FALSE); | 
 | 	if (!xresdir) { | 
 | 		TRACE(ver,"...no typeid entry found for %p\n",typeid); | 
 | 		HeapFree(GetProcessHeap(),0,image); | 
 | 		return 0; | 
 | 	} | 
 | 	xresdir = GetResDirEntryW(xresdir,resid,(DWORD)resourcedir,FALSE); | 
 | 	if (!xresdir) { | 
 | 		TRACE(ver,"...no resid entry found for %p\n",resid); | 
 | 		HeapFree(GetProcessHeap(),0,image); | 
 | 		return 0; | 
 | 	} | 
 | 	 | 
 | 	xresdir = GetResDirEntryW(xresdir,0,(DWORD)resourcedir,TRUE); | 
 | 	if (!xresdir) { | 
 | 		TRACE(ver,"...no 0 (default language) entry found for %p\n",resid); | 
 | 		HeapFree(GetProcessHeap(),0,image); | 
 | 		return 0; | 
 | 	} | 
 | 	xresdata = (PIMAGE_RESOURCE_DATA_ENTRY)xresdir; | 
 | 	*reslen	= xresdata->Size; | 
 | 	*resdata= (LPBYTE)xmalloc(*reslen); | 
 | 	memcpy(*resdata,image+xresdata->OffsetToData,*reslen); | 
 | 	/* find physical address for virtual offset */ | 
 | 	for (i=0;i<nrofsections;i++) { | 
 | 		if (sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) | 
 | 			continue; | 
 | 		if (	(xresdata->OffsetToData >= sections[i].VirtualAddress)&& | 
 | 			(xresdata->OffsetToData < sections[i].VirtualAddress+sections[i].SizeOfRawData) | 
 | 		) { | 
 | 			*off = (DWORD)(xresdata->OffsetToData)-(DWORD)(sections[i].VirtualAddress)+(DWORD)(sections[i].PointerToRawData); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	HeapFree(GetProcessHeap(),0,image); | 
 | 	HeapFree(GetProcessHeap(),0,sections); | 
 | 	TRACE(ver,"-- found at off=%lu\n",*off); | 
 | 	return 1; | 
 | } | 
 |  | 
 | /* GetFileResourceSize				[VER.2] */ | 
 | DWORD WINAPI GetFileResourceSize(LPCSTR filename,SEGPTR restype,SEGPTR resid, | 
 |                                  LPDWORD off) | 
 | { | 
 | 	HFILE32			lzfd; | 
 | 	OFSTRUCT		ofs; | 
 | 	BYTE			*resdata = NULL; | 
 | 	int			reslen=0; | 
 | 	int			res=0; | 
 |  | 
 | 	TRACE(ver,"(%s,type=0x%lx,id=0x%lx,off=%p)\n", | 
 | 		filename,(LONG)restype,(LONG)resid,off | 
 | 	); | 
 | 	lzfd=LZOpenFile32A(filename,&ofs,OF_READ); | 
 | 	if (!lzfd) | 
 | 		return 0; | 
 | 	switch (read_xx_header(lzfd)) { | 
 | 	case 0: | 
 | 	    res=0; | 
 | 	    break; | 
 | 	case IMAGE_OS2_SIGNATURE: | 
 | 	    res=find_ne_resource(lzfd,restype,resid,&resdata,&reslen,off); | 
 | 	    break; | 
 | 	case IMAGE_NT_SIGNATURE: | 
 | 	    res=find_pe_resource(lzfd,(LPWSTR)restype,(LPWSTR)resid,&resdata,&reslen,off); | 
 | 	    break; | 
 | 	} | 
 | 	if (!res) { | 
 | 	    LZClose32(lzfd); | 
 | 	    return 0; | 
 | 	} | 
 | 	if (resdata) | 
 | 		free(resdata); | 
 | 	LZClose32(lzfd); | 
 | 	return reslen; | 
 | } | 
 |  | 
 | /* GetFileResource				[VER.3] */ | 
 | DWORD WINAPI GetFileResource(LPCSTR filename,SEGPTR restype,SEGPTR resid, | 
 |                              DWORD off,DWORD datalen,LPVOID data ) | 
 | { | 
 | 	HFILE32			lzfd; | 
 | 	OFSTRUCT		ofs; | 
 | 	BYTE			*resdata=NULL; | 
 | 	int			res=0; | 
 | 	int			reslen=datalen; | 
 |  | 
 | 	TRACE(ver,"(%s,type=0x%lx,id=0x%lx,off=%ld,len=%ld,date=%p)\n", | 
 | 		filename,(LONG)restype,(LONG)resid,off,datalen,data | 
 | 	); | 
 |  | 
 | 	lzfd=LZOpenFile32A(filename,&ofs,OF_READ); | 
 | 	if (lzfd==0) | 
 | 		return 0; | 
 | 	if (!off) { | 
 | 		switch (read_xx_header(lzfd)) { | 
 | 		case 0:	res=0; | 
 | 			break; | 
 | 		case IMAGE_OS2_SIGNATURE: | 
 | 			res= find_ne_resource(lzfd,restype,resid,&resdata,&reslen,&off); | 
 | 			break; | 
 | 		case IMAGE_NT_SIGNATURE: | 
 | 			res= find_pe_resource(lzfd,(LPWSTR)restype,(LPWSTR)resid,&resdata,&reslen,&off); | 
 | 			break; | 
 | 		} | 
 | 		LZClose32(lzfd); | 
 | 		if (!res) | 
 | 			return 0; | 
 | 		if (reslen>datalen) reslen = datalen; | 
 | 		memcpy(data,resdata,reslen); | 
 | 		free(resdata); | 
 | 		TRACE(ver,"--[1] len=%u\n", reslen); | 
 | 		return reslen; | 
 | 	} | 
 | 	LZSeek32(lzfd,off,SEEK_SET); | 
 | 	reslen = LZRead32(lzfd,data,datalen); | 
 | 	LZClose32(lzfd); | 
 | 	TRACE(ver,"--[2] len=%u\n", reslen); | 
 | 	return reslen; | 
 | } | 
 |  | 
 | /* GetFileVersionInfoSize			[VER.6] */ | 
 | DWORD WINAPI GetFileVersionInfoSize16(LPCSTR filename,LPDWORD handle) | 
 | { | 
 | 	DWORD	len,ret,isuni=0; | 
 | 	BYTE	buf[144]; | 
 | 	VS_FIXEDFILEINFO *vffi; | 
 |  | 
 | 	TRACE(ver,"(%s,%p)\n",filename,handle); | 
 | 	len=GetFileResourceSize(filename,VS_FILE_INFO,VS_VERSION_INFO,handle); | 
 | 	if (!len) | 
 | 		return 0; | 
 | 	ret=GetFileResource( | 
 | 		filename,VS_FILE_INFO,VS_VERSION_INFO,*handle,sizeof(buf),buf | 
 | 	); | 
 | 	if (!ret) | 
 | 		return 0; | 
 |  | 
 | 	vffi=(VS_FIXEDFILEINFO*)(buf+0x14); | 
 | 	if (vffi->dwSignature != VS_FFI_SIGNATURE) { | 
 | 		/* unicode resource */ | 
 | 		if (vffi->dwSignature == 0x004f0049) { | 
 | 			isuni = 1; | 
 | 			vffi = (VS_FIXEDFILEINFO*)(buf+0x28); | 
 | 		} else { | 
 | 			WARN(ver,"vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n", | 
 | 				vffi->dwSignature,VS_FFI_SIGNATURE | 
 | 			); | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	if (*(WORD*)buf < len) | 
 | 		len = *(WORD*)buf; | 
 |  | 
 | 	if(TRACE_ON(ver)) | 
 | 	  print_vffi_debug(vffi); | 
 |  | 
 | 	return len; | 
 | } | 
 |  | 
 | /* GetFileVersionInfoSize32A			[VERSION.1] */ | 
 | DWORD WINAPI GetFileVersionInfoSize32A(LPCSTR filename,LPDWORD handle) | 
 | { | 
 | 	TRACE(ver,"(%s,%p)\n",filename,handle); | 
 | 	return GetFileVersionInfoSize16(filename,handle); | 
 | } | 
 |  | 
 | /* GetFileVersionInfoSize32W			[VERSION.2] */ | 
 | DWORD WINAPI GetFileVersionInfoSize32W( LPCWSTR filename, LPDWORD handle ) | 
 | { | 
 |     LPSTR xfn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     DWORD ret = GetFileVersionInfoSize16( xfn, handle ); | 
 |     HeapFree( GetProcessHeap(), 0, xfn ); | 
 |     return ret; | 
 | } | 
 |  | 
 | /* GetFileVersionInfo				[VER.7] */ | 
 | DWORD  WINAPI GetFileVersionInfo16(LPCSTR filename,DWORD handle,DWORD datasize, | 
 |                                    LPVOID data) | 
 | { | 
 |         TRACE(ver,"(%s,%ld,size=%ld,data=%p)\n", | 
 |                 filename,handle,datasize,data | 
 |         ); | 
 |         return GetFileResource( | 
 |                 filename,VS_FILE_INFO,VS_VERSION_INFO,handle,datasize,data | 
 |         ); | 
 | } | 
 |  | 
 | /* GetFileVersionInfoA				[VERSION.0] */ | 
 | DWORD  WINAPI GetFileVersionInfo32A(LPCSTR filename,DWORD handle, | 
 |                                     DWORD datasize,LPVOID data) | 
 | { | 
 | 	return GetFileVersionInfo16(filename,handle,datasize,data); | 
 | } | 
 |  | 
 | /* GetFileVersionInfoW				[VERSION.3] */ | 
 | DWORD WINAPI GetFileVersionInfo32W( LPCWSTR filename, DWORD handle, | 
 |                                     DWORD datasize, LPVOID data) | 
 | { | 
 |     LPSTR fn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     DWORD ret = GetFileVersionInfo16( fn, handle, datasize, data ); | 
 |     HeapFree( GetProcessHeap(), 0, fn ); | 
 |     return ret; | 
 | } | 
 |  | 
 | /***************************************************************************** | 
 |  * | 
 |  *   VerFindFile() [VER.8] | 
 |  *   Determines where to install a file based on whether it locates another | 
 |  *   version of the file in the system.  The values VerFindFile returns are | 
 |  *   used in a subsequent call to the VerInstallFile function. | 
 |  * | 
 |  *   Revision history: | 
 |  *      30-May-1997   Dave Cuthbert (dacut@ece.cmu.edu) | 
 |  *         Reimplementation of VerFindFile from original stub. | 
 |  * | 
 |  ****************************************************************************/ | 
 |  | 
 | DWORD WINAPI VerFindFile16( | 
 |     UINT16 flags, | 
 |     LPCSTR lpszFilename, | 
 |     LPCSTR lpszWinDir, | 
 |     LPCSTR lpszAppDir, | 
 |     LPSTR lpszCurDir, | 
 |     UINT16 *lpuCurDirLen, | 
 |     LPSTR lpszDestDir, | 
 |     UINT16 *lpuDestDirLen ) | 
 | { | 
 |     DWORD  retval; | 
 |     char  curDir[256]; | 
 |     char  destDir[256]; | 
 |     unsigned int  curDirSizeReq; | 
 |     unsigned int  destDirSizeReq; | 
 |  | 
 |     retval = 0; | 
 |  | 
 |     /* Print out debugging information */ | 
 |     TRACE(ver, "called with parameters:\n" | 
 | 		 "\tflags = %x", flags); | 
 |     if(flags & VFFF_ISSHAREDFILE) | 
 | 	TRACE(ver, " (VFFF_ISSHAREDFILE)\n"); | 
 |     else | 
 | 	TRACE(ver, "\n"); | 
 |  | 
 |     ver_dstring("\tlpszFilename = ", lpszFilename, ""); | 
 |     ver_dstring("\tlpszWinDir = ", lpszWinDir, ""); | 
 |     ver_dstring("\tlpszAppDir = ", lpszAppDir, ""); | 
 |  | 
 |     TRACE(ver, "\tlpszCurDir = %p\n", lpszCurDir); | 
 |     if(lpuCurDirLen) | 
 | 	TRACE(ver, "\tlpuCurDirLen = %p (%u)\n", | 
 | 		    lpuCurDirLen, *lpuCurDirLen); | 
 |     else | 
 | 	TRACE(ver, "\tlpuCurDirLen = (null)\n"); | 
 |  | 
 |     TRACE(ver, "\tlpszDestDir = %p\n", lpszDestDir); | 
 |     if(lpuDestDirLen) | 
 | 	TRACE(ver, "\tlpuDestDirLen = %p (%u)\n", | 
 | 		    lpuDestDirLen, *lpuDestDirLen); | 
 |  | 
 |     /* Figure out where the file should go; shared files default to the | 
 |        system directory */ | 
 |  | 
 |     strcpy(curDir, ""); | 
 |     strcpy(destDir, ""); | 
 |  | 
 |     if(flags & VFFF_ISSHAREDFILE) { | 
 | 	GetSystemDirectory32A(destDir, 256); | 
 |  | 
 | 	/* Were we given a filename?  If so, try to find the file. */ | 
 | 	if(lpszFilename) { | 
 | 	    if(testFileExistence(destDir, lpszFilename)) { | 
 | 		strcpy(curDir, destDir); | 
 |  | 
 | 		if(!testFileExclusiveExistence(destDir, lpszFilename)) | 
 | 		    retval |= VFF_FILEINUSE; | 
 | 	    } | 
 | 	    else if(lpszAppDir && testFileExistence(lpszAppDir, | 
 | 						    lpszFilename)) { | 
 | 		strcpy(curDir, lpszAppDir); | 
 | 		retval |= VFF_CURNEDEST; | 
 |  | 
 | 		if(!testFileExclusiveExistence(lpszAppDir, lpszFilename)) | 
 | 		    retval |= VFF_FILEINUSE; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |     else if(!(flags & VFFF_ISSHAREDFILE)) { /* not a shared file */ | 
 | 	if(lpszAppDir) { | 
 | 	    char  systemDir[256]; | 
 | 	    GetSystemDirectory32A(systemDir, 256); | 
 |  | 
 | 	    strcpy(destDir, lpszAppDir); | 
 |  | 
 | 	    if(lpszFilename) { | 
 | 		if(testFileExistence(lpszAppDir, lpszFilename)) { | 
 | 		    strcpy(curDir, lpszAppDir); | 
 |  | 
 | 		    if(!testFileExclusiveExistence(lpszAppDir, lpszFilename)) | 
 | 			retval |= VFF_FILEINUSE; | 
 | 		} | 
 | 		else if(testFileExistence(systemDir, lpszFilename)) { | 
 | 		    strcpy(curDir, systemDir); | 
 | 		    retval |= VFF_CURNEDEST; | 
 |  | 
 | 		    if(!testFileExclusiveExistence(systemDir, lpszFilename)) | 
 | 			retval |= VFF_FILEINUSE; | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |     curDirSizeReq = strlen(curDir) + 1; | 
 |     destDirSizeReq = strlen(destDir) + 1; | 
 |  | 
 |  | 
 |  | 
 |     /* Make sure that the pointers to the size of the buffers are | 
 |        valid; if not, do NOTHING with that buffer.  If that pointer | 
 |        is valid, then make sure that the buffer pointer is valid, too! */ | 
 |  | 
 |     if(lpuDestDirLen && lpszDestDir) { | 
 | 	if(*lpuDestDirLen < destDirSizeReq) { | 
 | 	    retval |= VFF_BUFFTOOSMALL; | 
 | 	    strncpy(lpszDestDir, destDir, *lpuDestDirLen - 1); | 
 | 	    lpszDestDir[*lpuDestDirLen - 1] = '\0'; | 
 | 	} | 
 | 	else | 
 | 	    strcpy(lpszDestDir, destDir); | 
 |  | 
 | 	*lpuDestDirLen = destDirSizeReq; | 
 |     } | 
 |      | 
 |     if(lpuCurDirLen && lpszCurDir) { | 
 | 	if(*lpuCurDirLen < curDirSizeReq) { | 
 | 	    retval |= VFF_BUFFTOOSMALL; | 
 | 	    strncpy(lpszCurDir, curDir, *lpuCurDirLen - 1); | 
 | 	    lpszCurDir[*lpuCurDirLen - 1] = '\0'; | 
 | 	} | 
 | 	else | 
 | 	    strcpy(lpszCurDir, curDir); | 
 |  | 
 | 	*lpuCurDirLen = curDirSizeReq; | 
 |     } | 
 |  | 
 |     TRACE(ver, "ret = %lu (%s%s%s)\n", retval, | 
 | 		 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "", | 
 | 		 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "", | 
 | 		 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : ""); | 
 |  | 
 |     ver_dstring("\t(Exit) lpszCurDir = ", lpszCurDir, ""); | 
 |     if(lpuCurDirLen) | 
 | 	TRACE(ver, "\t(Exit) lpuCurDirLen = %p (%u)\n", | 
 | 		    lpuCurDirLen, *lpuCurDirLen); | 
 |     else | 
 | 	TRACE(ver, "\t(Exit) lpuCurDirLen = (null)\n"); | 
 |  | 
 |     ver_dstring("\t(Exit) lpszDestDir = ", lpszDestDir, ""); | 
 |     if(lpuDestDirLen) | 
 | 	TRACE(ver, "\t(Exit) lpuDestDirLen = %p (%u)\n", | 
 | 		    lpuDestDirLen, *lpuDestDirLen); | 
 |  | 
 |     return retval; | 
 | } | 
 |  | 
 | /* VerFindFileA						[VERSION.5] */ | 
 | DWORD WINAPI VerFindFile32A( | 
 | 	UINT32 flags,LPCSTR filename,LPCSTR windir,LPCSTR appdir, | 
 | 	LPSTR curdir,UINT32 *pcurdirlen,LPSTR destdir,UINT32 *pdestdirlen ) | 
 | { | 
 |     UINT16 curdirlen, destdirlen; | 
 |     DWORD ret; | 
 |      | 
 |     curdirlen = (UINT16)*pcurdirlen; | 
 |     destdirlen= (UINT16)*pdestdirlen; | 
 |  | 
 |     ret = VerFindFile16(flags,filename,windir,appdir, | 
 |                         curdir,&curdirlen,destdir,&destdirlen); | 
 |     *pcurdirlen = curdirlen; | 
 |     *pdestdirlen = destdirlen; | 
 |     return ret; | 
 | } | 
 |  | 
 | /* VerFindFileW						[VERSION.6] */ | 
 | DWORD WINAPI VerFindFile32W( | 
 | 	UINT32 flags,LPCWSTR filename,LPCWSTR windir,LPCWSTR appdir, | 
 | 	LPWSTR curdir,UINT32 *pcurdirlen,LPWSTR destdir,UINT32 *pdestdirlen ) | 
 | { | 
 |     UINT16 curdirlen, destdirlen; | 
 |     LPSTR wfn,wwd,wad,wdd,wcd; | 
 |     DWORD ret; | 
 |  | 
 |     wfn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     wwd = HEAP_strdupWtoA( GetProcessHeap(), 0, windir ); | 
 |     wad = HEAP_strdupWtoA( GetProcessHeap(), 0, appdir ); | 
 |     wcd = HeapAlloc( GetProcessHeap(), 0, *pcurdirlen ); | 
 |     wdd = HeapAlloc( GetProcessHeap(), 0, *pdestdirlen ); | 
 |     ret = VerFindFile16(flags,wfn,wwd,wad,wcd,&curdirlen,wdd,&destdirlen); | 
 |     lstrcpynAtoW(curdir,wcd,*pcurdirlen); | 
 |     lstrcpynAtoW(destdir,wdd,*pdestdirlen); | 
 |     *pcurdirlen = strlen(wcd); | 
 |     *pdestdirlen = strlen(wdd); | 
 |     HeapFree( GetProcessHeap(), 0, wfn ); | 
 |     HeapFree( GetProcessHeap(), 0, wwd ); | 
 |     HeapFree( GetProcessHeap(), 0, wad ); | 
 |     HeapFree( GetProcessHeap(), 0, wcd ); | 
 |     HeapFree( GetProcessHeap(), 0, wdd ); | 
 |     return ret; | 
 | } | 
 |  | 
 | /* VerInstallFile					[VER.9] */ | 
 | DWORD WINAPI VerInstallFile16( | 
 | 	UINT16 flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir, | 
 |  	LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,UINT16 *tmpfilelen ) | 
 | { | 
 |     UINT32	filelen; | 
 |     DWORD ret= VerInstallFile32A(flags,srcfilename,destfilename,srcdir, | 
 |                                  destdir,curdir,tmpfile,&filelen); | 
 |  | 
 |     *tmpfilelen = filelen; | 
 |     return ret; | 
 | } | 
 |  | 
 | static LPBYTE | 
 | _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) { | 
 |     DWORD	alloclen; | 
 |     LPBYTE	buf; | 
 |     DWORD	ret; | 
 |  | 
 |     alloclen = 1000; | 
 |     buf= xmalloc(alloclen); | 
 |     while (1) { | 
 |     	ret = GetFileVersionInfo32A(fn,0,alloclen,buf); | 
 | 	if (!ret) { | 
 | 	    free(buf); | 
 | 	    return 0; | 
 | 	} | 
 | 	if (alloclen<*(WORD*)buf) { | 
 | 	    free(buf); | 
 | 	    alloclen = *(WORD*)buf; | 
 | 	    buf = xmalloc(alloclen); | 
 | 	} else { | 
 | 	    *vffi = (VS_FIXEDFILEINFO*)(buf+0x14); | 
 | 	    if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */ | 
 | 	    	*vffi = (VS_FIXEDFILEINFO*)(buf+0x28); | 
 | 	    if ((*vffi)->dwSignature != VS_FFI_SIGNATURE) | 
 | 	    	WARN(ver,"Bad VS_FIXEDFILEINFO signature 0x%08lx\n",(*vffi)->dwSignature); | 
 | 	    return buf; | 
 | 	} | 
 |     } | 
 | } | 
 |  | 
 | static DWORD | 
 | _error2vif(DWORD error) { | 
 |     switch (error) { | 
 |     case ERROR_ACCESS_DENIED: | 
 |     	return VIF_ACCESSVIOLATION; | 
 |     case ERROR_SHARING_VIOLATION: | 
 |     	return VIF_SHARINGVIOLATION; | 
 |     default: | 
 |     	return 0; | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * VerInstallFile32A [VERSION.7] | 
 |  */ | 
 | DWORD WINAPI VerInstallFile32A( | 
 | 	UINT32 flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir, | 
 |  	LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,UINT32 *tmpfilelen ) | 
 | { | 
 |     LPCSTR pdest; | 
 |     char	destfn[260],tmpfn[260],srcfn[260]; | 
 |     HFILE32	hfsrc,hfdst; | 
 |     DWORD	attr,ret,xret,tmplast; | 
 |     LPBYTE	buf1,buf2; | 
 |     OFSTRUCT	ofs; | 
 |  | 
 |     TRACE(ver,"(%x,%s,%s,%s,%s,%s,%p,%d)\n", | 
 | 	    flags,srcfilename,destfilename,srcdir,destdir,curdir,tmpfile,*tmpfilelen | 
 |     ); | 
 |     xret = 0; | 
 |     sprintf(srcfn,"%s\\%s",srcdir,srcfilename); | 
 |     if (!destdir || !*destdir) pdest = srcdir; | 
 |     else pdest = destdir; | 
 |     sprintf(destfn,"%s\\%s",pdest,destfilename); | 
 |     hfsrc=LZOpenFile32A(srcfn,&ofs,OF_READ); | 
 |     if (hfsrc==HFILE_ERROR32) | 
 |     	return VIF_CANNOTREADSRC; | 
 |     sprintf(tmpfn,"%s\\%s",pdest,destfilename); | 
 |     tmplast=strlen(pdest)+1; | 
 |     attr = GetFileAttributes32A(tmpfn); | 
 |     if (attr!=-1) { | 
 | 	if (attr & FILE_ATTRIBUTE_READONLY) { | 
 | 	    LZClose32(hfsrc); | 
 | 	    return VIF_WRITEPROT; | 
 | 	} | 
 | 	/* FIXME: check if file currently in use and return VIF_FILEINUSE */ | 
 |     } | 
 |     attr = -1; | 
 |     if (flags & VIFF_FORCEINSTALL) { | 
 |     	if (tmpfile[0]) { | 
 | 	    sprintf(tmpfn,"%s\\%s",pdest,tmpfile); | 
 | 	    tmplast = strlen(pdest)+1; | 
 | 	    attr = GetFileAttributes32A(tmpfn); | 
 | 	    /* if it exists, it has been copied by the call before. | 
 | 	     * we jump over the copy part...  | 
 | 	     */ | 
 | 	} | 
 |     } | 
 |     if (attr == -1) { | 
 |     	char	*s; | 
 |  | 
 | 	GetTempFileName32A(pdest,"ver",0,tmpfn); /* should not fail ... */ | 
 | 	s=strrchr(tmpfn,'\\'); | 
 | 	if (s) | 
 | 	    tmplast = s-tmpfn; | 
 | 	else | 
 | 	    tmplast = 0; | 
 | 	hfdst = OpenFile32(tmpfn,&ofs,OF_CREATE); | 
 | 	if (hfdst == HFILE_ERROR32) { | 
 | 	    LZClose32(hfsrc); | 
 | 	    return VIF_CANNOTCREATE; /* | translated dos error */ | 
 | 	} | 
 | 	ret = LZCopy32(hfsrc,hfdst); | 
 | 	_lclose32(hfdst); | 
 | 	if (((long) ret) < 0) { | 
 | 	    /* translate LZ errors into VIF_xxx */ | 
 | 	    switch (ret) { | 
 | 	    case LZERROR_BADINHANDLE: | 
 | 	    case LZERROR_READ: | 
 | 	    case LZERROR_BADVALUE: | 
 | 	    case LZERROR_UNKNOWNALG: | 
 | 		ret = VIF_CANNOTREADSRC; | 
 | 		break; | 
 | 	    case LZERROR_BADOUTHANDLE: | 
 | 	    case LZERROR_WRITE: | 
 | 		ret = VIF_OUTOFMEMORY; /* FIXME: correct? */ | 
 | 		break; | 
 | 	    case LZERROR_GLOBALLOC: | 
 | 	    case LZERROR_GLOBLOCK: | 
 | 		ret = VIF_OUTOFSPACE; | 
 | 		break; | 
 | 	    default: /* unknown error, should not happen */ | 
 | 		ret = 0; | 
 | 		break; | 
 | 	    } | 
 | 	    if (ret) { | 
 | 		LZClose32(hfsrc); | 
 | 		return ret; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |     xret = 0; | 
 |     if (!(flags & VIFF_FORCEINSTALL)) { | 
 | 	VS_FIXEDFILEINFO *destvffi,*tmpvffi; | 
 |     	buf1 = _fetch_versioninfo(destfn,&destvffi); | 
 | 	if (buf1) { | 
 | 	    buf2 = _fetch_versioninfo(tmpfn,&tmpvffi); | 
 | 	    if (buf2) { | 
 | 	    	char	*tbuf1,*tbuf2; | 
 | 		UINT32	len1,len2; | 
 |  | 
 | 		len1=len2=40; | 
 |  | 
 | 		/* compare file versions */ | 
 | 		if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)|| | 
 | 		    ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&& | 
 | 		     (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS) | 
 | 		    ) | 
 | 		) | 
 | 		    xret |= VIF_MISMATCH|VIF_SRCOLD; | 
 | 		/* compare filetypes and filesubtypes */ | 
 | 		if ((destvffi->dwFileType!=tmpvffi->dwFileType) || | 
 | 		    (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype) | 
 | 		) | 
 | 		    xret |= VIF_MISMATCH|VIF_DIFFTYPE; | 
 | 		if (VerQueryValue32A(buf1,"\\VarFileInfo\\Translation",(LPVOID*)&tbuf1,&len1) && | 
 | 		    VerQueryValue32A(buf2,"\\VarFileInfo\\Translation",(LPVOID*)&tbuf2,&len2) | 
 | 		) { | 
 | 		    /* irgendwas mit tbuf1 und tbuf2 machen  | 
 | 		     * generiert DIFFLANG|MISMATCH | 
 | 		     */ | 
 | 		} | 
 | 		free(buf2); | 
 | 	    } else | 
 | 		xret=VIF_MISMATCH|VIF_SRCOLD; | 
 | 	    free(buf1); | 
 | 	} | 
 |     } | 
 |     if (xret) { | 
 | 	if (*tmpfilelen<strlen(tmpfn+tmplast)) { | 
 | 	    xret|=VIF_BUFFTOOSMALL; | 
 | 	    DeleteFile32A(tmpfn); | 
 | 	} else { | 
 | 	    strcpy(tmpfile,tmpfn+tmplast); | 
 | 	    *tmpfilelen = strlen(tmpfn+tmplast)+1; | 
 | 	    xret|=VIF_TEMPFILE; | 
 | 	} | 
 |     } else { | 
 |     	if (-1!=GetFileAttributes32A(destfn)) | 
 | 	    if (!DeleteFile32A(destfn)) { | 
 | 		xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE; | 
 | 		DeleteFile32A(tmpfn); | 
 | 		LZClose32(hfsrc); | 
 | 		return xret; | 
 | 	    } | 
 | 	if ((!(flags & VIFF_DONTDELETEOLD))	&&  | 
 | 	    curdir				&&  | 
 | 	    *curdir				&& | 
 | 	    lstrcmpi32A(curdir,pdest) | 
 | 	) { | 
 | 	    char curfn[260]; | 
 |  | 
 | 	    sprintf(curfn,"%s\\%s",curdir,destfilename); | 
 | 	    if (-1!=GetFileAttributes32A(curfn)) { | 
 | 		/* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */ | 
 | 		if (!DeleteFile32A(curfn)) | 
 | 	    	    xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR; | 
 | 	    } | 
 | 	} | 
 | 	if (!MoveFile32A(tmpfn,destfn)) { | 
 | 	    xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME; | 
 | 	    DeleteFile32A(tmpfn); | 
 | 	} | 
 |     } | 
 |     LZClose32(hfsrc); | 
 |     return xret; | 
 | } | 
 |  | 
 |  | 
 | /* VerInstallFileW				[VERSION.8] */ | 
 | DWORD WINAPI VerInstallFile32W( | 
 | 	UINT32 flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir, | 
 | 	LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,UINT32 *tmpfilelen ) | 
 | { | 
 |     LPSTR wsrcf,wsrcd,wdestf,wdestd,wtmpf,wcurd; | 
 |     DWORD ret; | 
 |  | 
 |     wsrcf  = HEAP_strdupWtoA( GetProcessHeap(), 0, srcfilename ); | 
 |     wsrcd  = HEAP_strdupWtoA( GetProcessHeap(), 0, srcdir ); | 
 |     wdestf = HEAP_strdupWtoA( GetProcessHeap(), 0, destfilename ); | 
 |     wdestd = HEAP_strdupWtoA( GetProcessHeap(), 0, destdir ); | 
 |     wtmpf  = HEAP_strdupWtoA( GetProcessHeap(), 0, tmpfile ); | 
 |     wcurd  = HEAP_strdupWtoA( GetProcessHeap(), 0, curdir ); | 
 |     ret = VerInstallFile32A(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,tmpfilelen); | 
 |     if (!ret) | 
 |     	lstrcpynAtoW(tmpfile,wtmpf,*tmpfilelen); | 
 |     HeapFree( GetProcessHeap(), 0, wsrcf ); | 
 |     HeapFree( GetProcessHeap(), 0, wsrcd ); | 
 |     HeapFree( GetProcessHeap(), 0, wdestf ); | 
 |     HeapFree( GetProcessHeap(), 0, wdestd ); | 
 |     HeapFree( GetProcessHeap(), 0, wtmpf ); | 
 |     if (wcurd)  | 
 |     	HeapFree( GetProcessHeap(), 0, wcurd ); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | struct dbA { | 
 | 	WORD	nextoff; | 
 | 	WORD	datalen; | 
 | /* in memory structure... */ | 
 | 	char	name[1]; 	/* padded to dword alignment */ | 
 | /* ....  | 
 | 	char	data[datalen];     padded to dword alignment | 
 | 	BYTE	subdirdata[];      until nextoff | 
 |  */ | 
 | }; | 
 |  | 
 | #define DATA_OFFSET_A(db) ((4+(strlen((db)->name)+4))&~3) | 
 |  | 
 | struct dbW { | 
 | 	WORD	nextoff; | 
 | 	WORD	datalen; | 
 | 	WORD	btext;		/* type of data */ | 
 | /* in memory structure... */ | 
 | 	WCHAR	name[1]; 	/* padded to dword alignment */ | 
 | /* ....  | 
 | 	WCHAR	data[datalen];     padded to dword alignment | 
 | 	BYTE	subdirdata[];      until nextoff | 
 |  */ | 
 | }; | 
 |  | 
 | /* WORD nextoffset; | 
 |  * WORD datalength; | 
 |  * WORD btype; | 
 |  * WCHAR szKey[]; (zero terminated) | 
 |  * PADDING (round up to nearest 32bit boundary) | 
 |  */ | 
 | #define DATA_OFFSET_W(db) ((2+2+2+((lstrlen32W((db)->name)+1)*2+3))&~3) | 
 |  | 
 | /* this one used for Win16 resources, which are always in ASCII format */ | 
 | static BYTE* | 
 | _find_dataA(BYTE *block,LPCSTR str, int buff_remain) { | 
 | 	char	*nextslash; | 
 | 	int	substrlen, inc_size; | 
 | 	struct	dbA	*db; | 
 |  | 
 | 	while (*str && *str=='\\') | 
 | 		str++; | 
 | 	if (NULL!=(nextslash=strchr(str,'\\'))) | 
 | 		substrlen=nextslash-str; | 
 | 	else | 
 | 		substrlen=strlen(str); | 
 | 	if (nextslash!=NULL) { | 
 | 		while (*nextslash && *nextslash=='\\') | 
 | 			nextslash++; | 
 | 		if (!*nextslash) | 
 | 			nextslash=NULL; | 
 | 	} else if (*str == 0) | 
 | 		return NULL; | 
 |  | 
 |  | 
 | 	while (1) { | 
 | 		db=(struct dbA*)block; | 
 | 		TRACE(ver,"db=%p,db->nextoff=%d,db->datalen=%d,db->name=%s\n", | 
 | 			db,db->nextoff,db->datalen,db->name | 
 | 		); | 
 | 		if ((!db->nextoff) || (buff_remain<=0)) /* no more entries ? */ | 
 | 			return NULL; | 
 |  | 
 | 		TRACE(ver,"comparing with %s\n",db->name); | 
 | 		if (!lstrncmpi32A(db->name,str,substrlen)) { | 
 | 			if (nextslash) { | 
 | 				inc_size=DATA_OFFSET_A(db)+((db->datalen+3)&~3); | 
 | 				return _find_dataA(block+inc_size,nextslash, | 
 | 							db->nextoff-inc_size); | 
 | 			} else | 
 | 				return block; | 
 | 		} | 
 | 		inc_size	 = ((db->nextoff+3)&~3); | 
 | 		block		+= inc_size; | 
 | 		buff_remain	-= inc_size; | 
 | 	} | 
 | } | 
 |  | 
 | /* this one used for Win32 resources, which are always in UNICODE format */ | 
 | extern LPWSTR __cdecl CRTDLL_wcschr(LPCWSTR str,WCHAR xchar); | 
 | static BYTE* | 
 | _find_dataW(BYTE *block,LPCWSTR str, int buff_remain) { | 
 | 	LPWSTR	nextslash; | 
 | 	int	substrlen, inc_size; | 
 | 	struct	dbW	*db; | 
 |  | 
 |  | 
 | 	while (*str && *str=='\\') | 
 | 		str++; | 
 | 	if (NULL!=(nextslash=CRTDLL_wcschr(str,'\\'))) | 
 | 		substrlen=nextslash-str; | 
 | 	else | 
 | 		substrlen=lstrlen32W(str); | 
 | 	if (nextslash!=NULL) { | 
 | 		while (*nextslash && *nextslash=='\\') | 
 | 			nextslash++; | 
 | 		if (!*nextslash) | 
 | 			nextslash=NULL; | 
 | 	} else if (*str == 0) | 
 | 		return NULL; | 
 |  | 
 |  | 
 | 	while (1) { | 
 | 		char	*xs,*vs; | 
 | 		db=(struct dbW*)block; | 
 | 		xs= HEAP_strdupWtoA(GetProcessHeap(),0,db->name); | 
 | 		if (db->datalen) { | 
 | 			if (db->btext) | 
 | 				vs = HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR*)((block+DATA_OFFSET_W(db)))); | 
 | 			else | 
 | 				vs = HEAP_strdupA(GetProcessHeap(),0,"not a string"); | 
 | 		} else | 
 | 			vs = HEAP_strdupA(GetProcessHeap(),0,"no data"); | 
 |  | 
 | 		TRACE(ver,"db->nextoff=%d,db->name=%s,db->data=\"%s\"\n", | 
 | 			db->nextoff,xs,vs | 
 | 		); | 
 | 		HeapFree(GetProcessHeap(),0,vs); | 
 | 		HeapFree(GetProcessHeap(),0,xs); | 
 | 		if ((!db->nextoff) || (buff_remain<=0)) /* no more entries ? */ | 
 | 			return NULL; | 
 |  | 
 | 		if (!lstrncmpi32W(db->name,str,substrlen)) { | 
 | 			if (nextslash) { | 
 | 				/* DATA_OFFSET_W(db) (padded to 32bit already) | 
 | 				 * DATA[datalength] | 
 | 				 * PADDING (round up to nearest 32bit boundary) | 
 | 				 * -->	next level structs | 
 | 				 */ | 
 | 				inc_size=DATA_OFFSET_W(db)+((db->datalen+3)&~3); | 
 | 				return _find_dataW( block+inc_size ,nextslash, | 
 | 							db->nextoff-inc_size); | 
 | 			} else | 
 | 				return block; | 
 | 		} | 
 | 		/* skip over this block, round up to nearest 32bit boundary */ | 
 | 		inc_size	=  ((db->nextoff+3)&~3); | 
 | 		block		+= inc_size; | 
 | 		buff_remain	-= inc_size; | 
 | 	} | 
 | } | 
 |  | 
 | /* VerQueryValue 			[VER.11] */ | 
 | /* take care, 'buffer' is NOT a SEGPTR, it just points to one */ | 
 | DWORD WINAPI VerQueryValue16(SEGPTR segblock,LPCSTR subblock,SEGPTR *buffer, | 
 |                              UINT16 *buflen) | 
 | { | 
 | 	LPSTR	s; | 
 | 	BYTE	*block=PTR_SEG_TO_LIN(segblock),*b; | 
 |  | 
 | 	TRACE(ver,"(%p,%s,%p,%d)\n", | 
 | 		block,subblock,buffer,*buflen | 
 | 	); | 
 |  | 
 | 	s=(char*)xmalloc(strlen("VS_VERSION_INFO\\")+strlen(subblock)+1); | 
 | 	strcpy(s,"VS_VERSION_INFO\\");strcat(s,subblock); | 
 | 	/* check for UNICODE version */ | 
 | 	if (	(*(DWORD*)(block+0x14) != VS_FFI_SIGNATURE) &&  | 
 | 		(*(DWORD*)(block+0x28) == VS_FFI_SIGNATURE) | 
 | 	) { | 
 | 		struct	dbW	*db; | 
 | 		LPWSTR	wstr; | 
 | 		LPSTR	xs; | 
 |  | 
 | 		wstr = HEAP_strdupAtoW(GetProcessHeap(),0,s); | 
 | 		b=_find_dataW(block,wstr,*(WORD*)block); | 
 | 		HeapFree(GetProcessHeap(),0,wstr); | 
 | 		if (!b) { | 
 | 			WARN(ver,"key %s not found in versionresource.\n",s); | 
 | 			*buflen=0; | 
 | 			free (s); | 
 | 			return 0; | 
 | 		} | 
 | 		db=(struct dbW*)b; | 
 | 		b	= b+DATA_OFFSET_W(db); | 
 | 		*buflen	= db->datalen; | 
 | 		if (db->btext) { | 
 | 		    xs = HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR*)b); | 
 | 		    TRACE(ver,"->%s\n",xs); | 
 | 		    HeapFree(GetProcessHeap(),0,xs); | 
 | 		} else | 
 | 		    TRACE(ver,"->%p\n",b); | 
 | 	} else { | 
 | 		struct	dbA	*db; | 
 | 		b=_find_dataA(block,s,*(WORD*)block); | 
 | 		if (!b) { | 
 | 			WARN(ver,"key %s not found in versionresource.\n",s); | 
 | 			*buflen=0; | 
 | 			free (s); | 
 | 			return 0; | 
 | 		} | 
 | 		db=(struct dbA*)b; | 
 | 		b	= b+DATA_OFFSET_A(db); | 
 | 		*buflen	= db->datalen; | 
 | 		/* the string is only printable, if it is below \\StringFileInfo*/ | 
 | 		if (!lstrncmpi32A("VS_VERSION_INFO\\StringFileInfo\\",s,strlen("VS_VERSION_INFO\\StringFileInfo\\"))) | 
 | 		    TRACE(ver,"	-> %s=%s\n",subblock,b); | 
 | 		else | 
 | 		    TRACE(ver,"	-> %s=%p\n",subblock,b); | 
 | 	} | 
 | 	*buffer	= (b-block)+segblock; | 
 | 	free(s); | 
 | 	return 1; | 
 | } | 
 |  | 
 | DWORD WINAPI VerQueryValue32A(LPVOID vblock,LPCSTR subblock, | 
 |                               LPVOID *vbuffer,UINT32 *buflen) | 
 | { | 
 | 	BYTE	*b,*block=(LPBYTE)vblock,**buffer=(LPBYTE*)vbuffer; | 
 | 	LPSTR	s; | 
 |  | 
 | 	TRACE(ver,"(%p,%s,%p,%d)\n", | 
 | 		block,subblock,buffer,*buflen | 
 | 	); | 
 |  | 
 | 	s=(char*)xmalloc(strlen("VS_VERSION_INFO\\")+strlen(subblock)+1); | 
 | 	strcpy(s,"VS_VERSION_INFO\\");strcat(s,subblock); | 
 |  | 
 | 	/* check for UNICODE version */ | 
 | 	if (	(*(DWORD*)(block+0x14) != VS_FFI_SIGNATURE) &&  | 
 | 		(*(DWORD*)(block+0x28) == VS_FFI_SIGNATURE) | 
 | 	) { | 
 | 		LPWSTR	wstr; | 
 | 		LPSTR	xs; | 
 | 		struct	dbW	*db; | 
 |  | 
 | 		wstr = HEAP_strdupAtoW(GetProcessHeap(),0,s); | 
 | 		b=_find_dataW(block,wstr,*(WORD*)block); | 
 | 		HeapFree(GetProcessHeap(),0,wstr); | 
 | 		if (!b) { | 
 | 			WARN(ver,"key %s not found in versionresource.\n",s); | 
 | 			*buflen=0; | 
 | 			free (s); | 
 | 			return 0; | 
 | 		} | 
 | 		db	= (struct dbW*)b; | 
 | 		*buflen	= db->datalen; | 
 | 		b	= b+DATA_OFFSET_W(db); | 
 | 		if (db->btext) { | 
 | 		    xs = HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR*)b); | 
 | 		    TRACE(ver,"->%s\n",xs); | 
 | 		    HeapFree(GetProcessHeap(),0,xs); | 
 |  | 
 | 		    /* This is a leak.  */ | 
 | 		    b = HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR*)b); | 
 | 		} else | 
 | 		    TRACE(ver,"->%p\n",b); | 
 | 	} else { | 
 | 		struct	dbA	*db; | 
 | 		b=_find_dataA(block,s,*(WORD*)block); | 
 | 		if (!b) { | 
 | 			WARN(ver,"key %s not found in versionresource.\n",subblock); | 
 | 			*buflen=0; | 
 | 			free (s); | 
 | 			return 0; | 
 | 		} | 
 | 		db=(struct dbA*)b; | 
 | 		*buflen	= db->datalen; | 
 | 		b	= b+DATA_OFFSET_A(db); | 
 |  | 
 | 		/* the string is only printable, if it is below \\StringFileInfo*/ | 
 | 		if (!lstrncmpi32A("VS_VERSION_INFO\\StringFileInfo\\",s,strlen("VS_VERSION_INFO\\StringFileInfo\\"))) | 
 | 		    TRACE(ver,"	-> %s=%s\n",subblock,b); | 
 | 		else | 
 | 		    TRACE(ver,"	-> %s=%p\n",subblock,b); | 
 | 	} | 
 | 	*buffer	= b; | 
 | 	free(s); | 
 | 	return 1; | 
 | } | 
 |  | 
 | DWORD WINAPI VerQueryValue32W(LPVOID vblock,LPCWSTR subblock,LPVOID *vbuffer, | 
 |                               UINT32 *buflen) | 
 | { | 
 | 	LPSTR		sb; | 
 | 	DWORD		ret; | 
 |  | 
 | 	sb = HEAP_strdupWtoA( GetProcessHeap(), 0, subblock ); | 
 | 	ret = VerQueryValue32A(vblock,sb,vbuffer,buflen); | 
 |         HeapFree( GetProcessHeap(), 0, sb ); | 
 | 	return 1; | 
 | } | 
 | /* 20 GETFILEVERSIONINFORAW */ |