Release 980301
[wine] / misc / ver.c
1 /* 
2  * Implementation of VER.DLL
3  * 
4  * Copyright 1996,1997 Marcus Meissner
5  * Copyright 1997 David Cuthbert
6  */
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <unistd.h>
12 #include "windows.h"
13 #include "win.h"
14 #include "winerror.h"
15 #include "heap.h"
16 #include "ver.h"
17 #include "lzexpand.h"
18 #include "module.h"
19 #include "neexe.h"
20 #include "debug.h"
21 #include "xmalloc.h"
22 #include "winreg.h"
23
24 #define LZREAD(what) \
25   if (sizeof(*what)!=LZRead32(lzfd,what,sizeof(*what))) return 0;
26 #define LZTELL(lzfd) LZSeek32(lzfd, 0, SEEK_CUR);
27
28 /******************************************************************************
29  *
30  *   void  ver_dstring(
31  *      char const * prologue,
32  *      char const * teststring,
33  *      char const * epilogue )
34  *
35  *   This function will print via dprintf[_]ver to stddeb the prologue string,
36  *   followed by the address of teststring and the string it contains if
37  *   teststring is non-null or "(null)" otherwise, and then the epilogue
38  *   string followed by a new line.
39  *
40  *   Revision history
41  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
42  *         Original implementation as dprintf[_]ver_string
43  *      05-Jul-1997 Dave Cuthbert (dacut@ece.cmu.edu)
44  *         Fixed problem that caused bug with tools/make_debug -- renaming
45  *         this function should fix the problem.
46  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
47  *         Modified it to make it print the message using only one
48  *         dprintf[_]ver call.
49  *
50  *****************************************************************************/
51
52 static void  ver_dstring(
53     char const * prologue,
54     char const * teststring,
55     char const * epilogue )
56 {
57     dprintf_info(ver, "%s %p (\"%s\") %s\n", prologue, 
58                 (void const *) teststring, 
59                 teststring ? teststring : "(null)",
60                 epilogue);
61 }
62
63 /******************************************************************************
64  *
65  *   This function will print via dprintf[_]ver to stddeb debug info regarding
66  *   the file info structure vffi.
67  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
68  *      Added this function to clean up the code.
69  *
70  *****************************************************************************/
71 static void print_vffi_debug(VS_FIXEDFILEINFO *vffi)
72 {
73         dbg_decl_str(ver, 1024);
74
75         dprintf_info(ver," structversion=0x%lx.0x%lx, fileversion=0x%lx.0x%lx, productversion=0x%lx.0x%lx, flagmask=0x%lx, flags=%s%s%s%s%s%s\n",
76                     (vffi->dwStrucVersion>>16),vffi->dwStrucVersion&0xFFFF,
77                     vffi->dwFileVersionMS,vffi->dwFileVersionLS,
78                     vffi->dwProductVersionMS,vffi->dwProductVersionLS,
79                     vffi->dwFileFlagsMask,
80                     (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
81                     (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
82                     (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
83                     (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
84                     (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
85                     (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : ""
86                     );
87
88         dsprintf(ver," OS=0x%lx.0x%lx ",
89                 (vffi->dwFileOS&0xFFFF0000)>>16,
90                 vffi->dwFileOS&0x0000FFFF
91         );
92         switch (vffi->dwFileOS&0xFFFF0000) {
93         case VOS_DOS:dsprintf(ver,"DOS,");break;
94         case VOS_OS216:dsprintf(ver,"OS/2-16,");break;
95         case VOS_OS232:dsprintf(ver,"OS/2-32,");break;
96         case VOS_NT:dsprintf(ver,"NT,");break;
97         case VOS_UNKNOWN:
98         default:
99                 dsprintf(ver,"UNKNOWN(0x%lx),",vffi->dwFileOS&0xFFFF0000);break;
100         }
101         switch (vffi->dwFileOS & 0xFFFF) {
102         case VOS__BASE:dsprintf(ver,"BASE");break;
103         case VOS__WINDOWS16:dsprintf(ver,"WIN16");break;
104         case VOS__WINDOWS32:dsprintf(ver,"WIN32");break;
105         case VOS__PM16:dsprintf(ver,"PM16");break;
106         case VOS__PM32:dsprintf(ver,"PM32");break;
107         default:dsprintf(ver,"UNKNOWN(0x%lx)",vffi->dwFileOS&0xFFFF);break;
108         }
109         dprintf_info(ver, "(%s)\n", dbg_str(ver));
110
111         dbg_reset_str(ver);
112         switch (vffi->dwFileType) {
113         default:
114         case VFT_UNKNOWN:
115                 dsprintf(ver,"filetype=Unknown(0x%lx)",vffi->dwFileType);
116                 break;
117         case VFT_APP:dsprintf(ver,"filetype=APP,");break;
118         case VFT_DLL:dsprintf(ver,"filetype=DLL,");break;
119         case VFT_DRV:
120                 dsprintf(ver,"filetype=DRV,");
121                 switch(vffi->dwFileSubtype) {
122                 default:
123                 case VFT2_UNKNOWN:
124                         dsprintf(ver,"UNKNOWN(0x%lx)",vffi->dwFileSubtype);
125                         break;
126                 case VFT2_DRV_PRINTER:
127                         dsprintf(ver,"PRINTER");
128                         break;
129                 case VFT2_DRV_KEYBOARD:
130                         dsprintf(ver,"KEYBOARD");
131                         break;
132                 case VFT2_DRV_LANGUAGE:
133                         dsprintf(ver,"LANGUAGE");
134                         break;
135                 case VFT2_DRV_DISPLAY:
136                         dsprintf(ver,"DISPLAY");
137                         break;
138                 case VFT2_DRV_MOUSE:
139                         dsprintf(ver,"MOUSE");
140                         break;
141                 case VFT2_DRV_NETWORK:
142                         dsprintf(ver,"NETWORK");
143                         break;
144                 case VFT2_DRV_SYSTEM:
145                         dsprintf(ver,"SYSTEM");
146                         break;
147                 case VFT2_DRV_INSTALLABLE:
148                         dsprintf(ver,"INSTALLABLE");
149                         break;
150                 case VFT2_DRV_SOUND:
151                         dsprintf(ver,"SOUND");
152                         break;
153                 case VFT2_DRV_COMM:
154                         dsprintf(ver,"COMM");
155                         break;
156                 case VFT2_DRV_INPUTMETHOD:
157                         dsprintf(ver,"INPUTMETHOD");
158                         break;
159                 }
160                 break;
161         case VFT_FONT:
162                 dsprintf(ver,"filetype=FONT.");
163                 switch (vffi->dwFileSubtype) {
164                 default:
165                         dsprintf(ver,"UNKNOWN(0x%lx)",vffi->dwFileSubtype);
166                         break;
167                 case VFT2_FONT_RASTER:dsprintf(ver,"RASTER");break;
168                 case VFT2_FONT_VECTOR:dsprintf(ver,"VECTOR");break;
169                 case VFT2_FONT_TRUETYPE:dsprintf(ver,"TRUETYPE");break;
170                 }
171                 break;
172         case VFT_VXD:dsprintf(ver,"filetype=VXD");break;
173         case VFT_STATIC_LIB:dsprintf(ver,"filetype=STATIC_LIB");break;
174         }
175         dprintf_info(ver, "%s\n", dbg_str(ver));
176
177         dprintf_info(ver, "  filedata=0x%lx.0x%lx\n",
178                     vffi->dwFileDateMS,vffi->dwFileDateLS);
179 }
180
181 /******************************************************************************
182  *
183  *   int  testFileExistence(
184  *      char const * path,
185  *      char const * file )
186  *
187  *   Tests whether a given path/file combination exists.  If the file does
188  *   not exist, the return value is zero.  If it does exist, the return
189  *   value is non-zero.
190  *
191  *   Revision history
192  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
193  *         Original implementation
194  *
195  *****************************************************************************/
196
197 static int  testFileExistence(
198    char const * path,
199    char const * file )
200 {
201     char  filename[1024];
202     int  filenamelen;
203     OFSTRUCT  fileinfo;
204     int  retval;
205
206     fileinfo.cBytes = sizeof(OFSTRUCT);
207
208     strcpy(filename, path);
209     filenamelen = strlen(filename);
210
211     /* Add a trailing \ if necessary */
212     if(filenamelen) {
213         if(filename[filenamelen - 1] != '\\')
214             strcat(filename, "\\");
215     }
216     else /* specify the current directory */
217         strcpy(filename, ".\\");
218
219     /* Create the full pathname */
220     strcat(filename, file);
221
222     if(OpenFile32(filename, &fileinfo, OF_EXIST) == HFILE_ERROR32)
223         retval = 0;
224     else
225         retval = 1;
226
227     return  retval;
228 }
229
230 /******************************************************************************
231  *
232  *   int  testFileExclusiveExistence(
233  *      char const * path,
234  *      char const * file )
235  *
236  *   Tests whether a given path/file combination exists and ensures that no
237  *   other programs have handles to the given file.  If the file does not
238  *   exist or is open, the return value is zero.  If it does exist, the
239  *   return value is non-zero.
240  *
241  *   Revision history
242  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
243  *         Original implementation
244  *
245  *****************************************************************************/
246
247 static int  testFileExclusiveExistence(
248    char const * path,
249    char const * file )
250 {
251     char  filename[1024];
252     int  filenamelen;
253     OFSTRUCT  fileinfo;
254     int  retval;
255
256     fileinfo.cBytes = sizeof(OFSTRUCT);
257
258     strcpy(filename, path);
259     filenamelen = strlen(filename);
260
261     /* Add a trailing \ if necessary */
262     if(filenamelen) {
263         if(filename[filenamelen - 1] != '\\')
264             strcat(filename, "\\");
265     }
266     else /* specify the current directory */
267         strcpy(filename, ".\\");
268
269     /* Create the full pathname */
270     strcat(filename, file);
271
272     if(OpenFile32(filename, &fileinfo, OF_EXIST | OF_SHARE_EXCLUSIVE) ==
273        HFILE_ERROR32)
274         retval = 0;
275     else
276         retval = 1;
277
278     return retval;
279 }
280
281
282 static int read_xx_header(HFILE32 lzfd) {
283         IMAGE_DOS_HEADER        mzh;
284         char                    magic[3];
285
286         LZSeek32(lzfd,0,SEEK_SET);
287         if (sizeof(mzh)!=LZRead32(lzfd,&mzh,sizeof(mzh)))
288                 return 0;
289         if (mzh.e_magic!=IMAGE_DOS_SIGNATURE)
290                 return 0;
291         LZSeek32(lzfd,mzh.e_lfanew,SEEK_SET);
292         if (2!=LZRead32(lzfd,magic,2))
293                 return 0;
294         LZSeek32(lzfd,mzh.e_lfanew,SEEK_SET);
295         if (magic[0] == 'N' && magic[1] == 'E')
296                 return IMAGE_OS2_SIGNATURE;
297         if (magic[0] == 'P' && magic[1] == 'E')
298                 return IMAGE_NT_SIGNATURE;
299         magic[2]='\0';
300         fprintf(stderr,"misc/ver.c:read_ne_header:can't handle %s files.\n",magic);
301         return 0;
302 }
303
304
305 static int find_ne_resource(
306         HFILE32 lzfd,SEGPTR typeid,SEGPTR resid,
307         BYTE **resdata,int *reslen,DWORD *off
308 ) {
309         IMAGE_OS2_HEADER nehd;
310         NE_TYPEINFO     ti;
311         NE_NAMEINFO     ni;
312         int             i;
313         WORD            shiftcount;
314         DWORD           nehdoffset;
315
316         nehdoffset = LZTELL(lzfd);
317         LZREAD(&nehd);
318         if (nehd.resource_tab_offset==nehd.rname_tab_offset) {
319                 dprintf_info(ver,"no resources in NE dll\n");
320                 return 0;
321         }
322         LZSeek32(lzfd,nehd.resource_tab_offset+nehdoffset,SEEK_SET);
323         LZREAD(&shiftcount);
324         dprintf_info(ver,"shiftcount is %d\n",shiftcount);
325         dprintf_info(ver,"reading resource typeinfo dir.\n");
326
327         if (!HIWORD(typeid)) typeid = (SEGPTR)(LOWORD(typeid) | 0x8000);
328         if (!HIWORD(resid))  resid  = (SEGPTR)(LOWORD(resid) | 0x8000);
329         while (1) {
330                 int     skipflag;
331
332                 LZREAD(&ti);
333                 if (!ti.type_id)
334                         return 0;
335                 dprintf_info(ver,"    ti.typeid =%04x,count=%d\n",ti.type_id,ti.count);
336
337                 skipflag=0;
338                 if (!HIWORD(typeid)) {
339                         if ((ti.type_id&0x8000)&&(typeid!=ti.type_id))
340                                 skipflag=1;
341                 } else {
342                         if (ti.type_id & 0x8000) {
343                                 skipflag=1; 
344                         } else {
345                                 BYTE    len;
346                                 char    *str;
347                                 DWORD   whereleft;
348
349                                 whereleft = LZTELL(lzfd);
350                                 LZSeek32(
351                                         lzfd,
352                                         nehdoffset+nehd.resource_tab_offset+ti.type_id,
353                                         SEEK_SET
354                                 );
355                                 LZREAD(&len);
356                                 str=xmalloc(len);
357                                 if (len!=LZRead32(lzfd,str,len))
358                                         return 0;
359                                 dprintf_info(ver,"read %s to compare it with %s\n",
360                                         str,(char*)PTR_SEG_TO_LIN(typeid)
361                                 );
362                                 if (lstrcmpi32A(str,(char*)PTR_SEG_TO_LIN(typeid)))
363                                         skipflag=1;
364                                 free(str);
365                                 LZSeek32(lzfd,whereleft,SEEK_SET);
366                         }
367                 }
368                 if (skipflag) {
369                         LZSeek32(lzfd,ti.count*sizeof(ni),SEEK_CUR);
370                         continue;
371                 }
372                 for (i=0;i<ti.count;i++) {
373                         WORD    *rdata;
374                         int     len;
375
376                         LZREAD(&ni);
377                         dprintf_info(ver,"      ni.id=%4x,offset=%d,length=%d\n",
378                                 ni.id,ni.offset,ni.length
379                         );
380                         skipflag=1;
381                         if (!HIWORD(resid)) {
382                                 if (ni.id == resid)
383                                         skipflag=0;
384                         } else {
385                                 if (!(ni.id & 0x8000)) {
386                                         BYTE    len;
387                                         char    *str;
388                                         DWORD   whereleft;
389
390                                         whereleft = LZTELL(lzfd);
391                                           LZSeek32(
392                                                 lzfd,
393                                                 nehdoffset+nehd.resource_tab_offset+ni.id,
394                                                 SEEK_SET
395                                         );
396                                         LZREAD(&len);
397                                         str=xmalloc(len);
398                                         if (len!=LZRead32(lzfd,str,len))
399                                                 return 0;
400                                         dprintf_info(ver,"read %s to compare it with %s\n",
401                                                 str,(char*)PTR_SEG_TO_LIN(typeid)
402                                         );
403                                         if (!lstrcmpi32A(str,(char*)PTR_SEG_TO_LIN(typeid)))
404                                                 skipflag=0;
405                                         free(str);
406                                         LZSeek32(lzfd,whereleft,SEEK_SET);
407                                 }
408                         }
409                         if (skipflag)
410                                 continue;
411                         LZSeek32(lzfd,((int)ni.offset<<shiftcount),SEEK_SET);
412                         *off    = (int)ni.offset<<shiftcount;
413                         len     = ni.length<<shiftcount;
414                         rdata=(WORD*)xmalloc(len);
415                         if (len!=LZRead32(lzfd,rdata,len)) {
416                                 free(rdata);
417                                 return 0;
418                         }
419                         dprintf_info(ver,"resource found.\n");
420                         *resdata= (BYTE*)rdata;
421                         *reslen = len;
422                         return 1;
423                 }
424         }
425 }
426
427 /* Loads the specified PE resource.
428  * FIXME: shouldn't load the whole image
429  */
430 static int
431 find_pe_resource(
432         HFILE32 lzfd,LPWSTR typeid,LPWSTR resid,
433         BYTE **resdata,int *reslen,DWORD *off
434 ) {
435         IMAGE_NT_HEADERS pehd;
436         int             i;
437         UINT32          nrofsections;
438         DWORD           imagesize,pehdoffset;
439         BYTE            *image;
440         IMAGE_DATA_DIRECTORY            resdir;
441         LPIMAGE_RESOURCE_DIRECTORY      resourcedir,xresdir;
442         LPIMAGE_RESOURCE_DATA_ENTRY     xresdata;
443         LPIMAGE_SECTION_HEADER          sections;
444
445         pehdoffset = LZTELL(lzfd);
446         LZREAD(&pehd);
447         resdir = pehd.OptionalHeader.DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY];
448         dprintf_info(ver,"find_pe_resource(.,%p,%p,....)\n",typeid,resid);
449         if (!resdir.Size) {
450                 fprintf(stderr,"misc/ver.c:find_pe_resource() no resource directory found in PE file.\n");
451                 return 0;
452         }
453         imagesize = pehd.OptionalHeader.SizeOfImage;
454         image = HeapAlloc(GetProcessHeap(),0,imagesize);
455         nrofsections = pehd.FileHeader.NumberOfSections;
456
457         sections = (LPIMAGE_SECTION_HEADER)HeapAlloc(GetProcessHeap(),0,pehd.FileHeader.NumberOfSections*sizeof(IMAGE_SECTION_HEADER));
458         LZSeek32(lzfd,
459                 pehdoffset+
460                 sizeof(DWORD)+  /* Signature */
461                 sizeof(IMAGE_FILE_HEADER)+      
462                 pehd.FileHeader.SizeOfOptionalHeader,
463                 SEEK_SET
464         );
465         if (    nrofsections*sizeof(IMAGE_SECTION_HEADER)!=
466                 LZRead32(lzfd,sections,nrofsections*sizeof(IMAGE_SECTION_HEADER))
467         ) {
468                 HeapFree(GetProcessHeap(),0,image);
469                 return 0;
470         }
471         for (i=0;i<nrofsections;i++) {
472                 if (sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
473                         continue;
474                 LZSeek32(lzfd,sections[i].PointerToRawData,SEEK_SET);
475                 if (    sections[i].SizeOfRawData!=
476                         LZRead32(lzfd,image+sections[i].VirtualAddress,sections[i].SizeOfRawData)
477                 )
478                         continue;
479         }
480         resourcedir = (LPIMAGE_RESOURCE_DIRECTORY)(image+resdir.VirtualAddress);
481         xresdir = GetResDirEntryW(resourcedir,typeid,(DWORD)resourcedir,FALSE);
482         if (!xresdir) {
483                 dprintf_info(ver,"...no typeid entry found for %p\n",typeid);
484                 HeapFree(GetProcessHeap(),0,image);
485                 return 0;
486         }
487         xresdir = GetResDirEntryW(xresdir,resid,(DWORD)resourcedir,FALSE);
488         if (!xresdir) {
489                 dprintf_info(ver,"...no resid entry found for %p\n",resid);
490                 HeapFree(GetProcessHeap(),0,image);
491                 return 0;
492         }
493         
494         xresdir = GetResDirEntryW(xresdir,0,(DWORD)resourcedir,TRUE);
495         if (!xresdir) {
496                 dprintf_info(ver,"...no 0 (default language) entry found for %p\n",resid);
497                 HeapFree(GetProcessHeap(),0,image);
498                 return 0;
499         }
500         xresdata = (LPIMAGE_RESOURCE_DATA_ENTRY)xresdir;
501         *reslen = xresdata->Size;
502         *resdata= (LPBYTE)xmalloc(*reslen);
503         memcpy(*resdata,image+xresdata->OffsetToData,*reslen);
504         /* find physical address for virtual offset */
505         for (i=0;i<nrofsections;i++) {
506                 if (sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
507                         continue;
508                 if (    (xresdata->OffsetToData >= sections[i].VirtualAddress)&&
509                         (xresdata->OffsetToData < sections[i].VirtualAddress+sections[i].SizeOfRawData)
510                 ) {
511                         *off = (DWORD)(xresdata->OffsetToData)-(DWORD)(sections[i].VirtualAddress)+(DWORD)(sections[i].PointerToRawData);
512                         break;
513                 }
514         }
515         HeapFree(GetProcessHeap(),0,image);
516         HeapFree(GetProcessHeap(),0,sections);
517         return 1;
518 }
519
520 /* GetFileResourceSize                          [VER.2] */
521 DWORD WINAPI GetFileResourceSize(LPCSTR filename,SEGPTR restype,SEGPTR resid,
522                                  LPDWORD off)
523 {
524         HFILE32                 lzfd;
525         OFSTRUCT                ofs;
526         BYTE                    *resdata = NULL;
527         int                     reslen=0;
528         int                     res=0;
529
530         dprintf_info(ver,"GetFileResourceSize(%s,%lx,%lx,%p)\n",
531                 filename,(LONG)restype,(LONG)resid,off
532         );
533         lzfd=LZOpenFile32A(filename,&ofs,OF_READ);
534         if (!lzfd)
535                 return 0;
536         switch (read_xx_header(lzfd)) {
537         case 0:
538             res=0;
539             break;
540         case IMAGE_OS2_SIGNATURE:
541             res=find_ne_resource(lzfd,restype,resid,&resdata,&reslen,off);
542             break;
543         case IMAGE_NT_SIGNATURE:
544             res=find_pe_resource(lzfd,(LPWSTR)restype,(LPWSTR)resid,&resdata,&reslen,off);
545             break;
546         }
547         if (!res) {
548             LZClose32(lzfd);
549             return 0;
550         }
551         if (resdata)
552                 free(resdata);
553         LZClose32(lzfd);
554         return reslen;
555 }
556
557 /* GetFileResource                              [VER.3] */
558 DWORD WINAPI GetFileResource(LPCSTR filename,SEGPTR restype,SEGPTR resid,
559                              DWORD off,DWORD datalen,LPVOID data )
560 {
561         HFILE32                 lzfd;
562         OFSTRUCT                ofs;
563         BYTE                    *resdata=NULL;
564         int                     res=0;
565         int                     reslen=datalen;
566
567         dprintf_info(ver,"GetFileResource(%s,%lx,%lx,%ld,%ld,%p)\n",
568                 filename,(LONG)restype,(LONG)resid,off,datalen,data
569         );
570
571         lzfd=LZOpenFile32A(filename,&ofs,OF_READ);
572         if (lzfd==0)
573                 return 0;
574         if (!off) {
575                 switch (read_xx_header(lzfd)) {
576                 case 0: res=0;
577                         break;
578                 case IMAGE_OS2_SIGNATURE:
579                         res= find_ne_resource(lzfd,restype,resid,&resdata,&reslen,&off);
580                         break;
581                 case IMAGE_NT_SIGNATURE:
582                         res= find_pe_resource(lzfd,(LPWSTR)restype,(LPWSTR)resid,&resdata,&reslen,&off);
583                         break;
584                 }
585                 LZClose32(lzfd);
586                 if (!res)
587                         return 0;
588                 if (reslen>datalen) reslen = datalen;
589                 memcpy(data,resdata,reslen);
590                 free(resdata);
591                 return reslen;
592         }
593         LZSeek32(lzfd,off,SEEK_SET);
594         reslen = LZRead32(lzfd,data,datalen);
595         LZClose32(lzfd);
596         return reslen;
597 }
598
599 /* GetFileVersionInfoSize                       [VER.6] */
600 DWORD WINAPI GetFileVersionInfoSize16(LPCSTR filename,LPDWORD handle)
601 {
602         DWORD   len,ret,isuni=0;
603         BYTE    buf[144];
604         VS_FIXEDFILEINFO *vffi;
605
606         dprintf_info(ver,"GetFileVersionInfoSize16(%s,%p)\n",filename,handle);
607         len=GetFileResourceSize(filename,VS_FILE_INFO,VS_VERSION_INFO,handle);
608         if (!len)
609                 return 0;
610         ret=GetFileResource(
611                 filename,VS_FILE_INFO,VS_VERSION_INFO,*handle,sizeof(buf),buf
612         );
613         if (!ret)
614                 return 0;
615
616         vffi=(VS_FIXEDFILEINFO*)(buf+0x14);
617         if (vffi->dwSignature != VS_FFI_SIGNATURE) {
618                 /* unicode resource */
619                 if (vffi->dwSignature == 0x004f0049) {
620                         isuni = 1;
621                         vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
622                 } else {
623                         fprintf(stderr,"vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n",
624                                 vffi->dwSignature,VS_FFI_SIGNATURE
625                         );
626                         return 0;
627                 }
628         }
629         if (*(WORD*)buf < len)
630                 len = *(WORD*)buf;
631
632         if(debugging_info(ver))
633           print_vffi_debug(vffi);
634
635         return len;
636 }
637
638 /* GetFileVersionInfoSize32A                    [VERSION.1] */
639 DWORD WINAPI GetFileVersionInfoSize32A(LPCSTR filename,LPDWORD handle)
640 {
641         dprintf_info(ver,"GetFileVersionInfoSize32A(%s,%p)\n",filename,handle);
642         return GetFileVersionInfoSize16(filename,handle);
643 }
644
645 /* GetFileVersionInfoSize32W                    [VERSION.2] */
646 DWORD WINAPI GetFileVersionInfoSize32W( LPCWSTR filename, LPDWORD handle )
647 {
648     LPSTR xfn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
649     DWORD ret = GetFileVersionInfoSize16( xfn, handle );
650     HeapFree( GetProcessHeap(), 0, xfn );
651     return ret;
652 }
653
654 /* GetFileVersionInfo                           [VER.7] */
655 DWORD  WINAPI GetFileVersionInfo16(LPCSTR filename,DWORD handle,DWORD datasize,
656                                    LPVOID data)
657 {
658         dprintf_info(ver,"GetFileVersionInfo16(%s,%ld,%ld,%p)\n",
659                 filename,handle,datasize,data
660         );
661         return GetFileResource(
662                 filename,VS_FILE_INFO,VS_VERSION_INFO,handle,datasize,data
663         );
664 }
665
666 /* GetFileVersionInfoA                          [VERSION.0] */
667 DWORD  WINAPI GetFileVersionInfo32A(LPCSTR filename,DWORD handle,
668                                     DWORD datasize,LPVOID data)
669 {
670         return GetFileVersionInfo16(filename,handle,datasize,data);
671 }
672
673 /* GetFileVersionInfoW                          [VERSION.3] */
674 DWORD WINAPI GetFileVersionInfo32W( LPCWSTR filename, DWORD handle,
675                                     DWORD datasize, LPVOID data)
676 {
677     LPSTR fn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
678     DWORD ret = GetFileVersionInfo16( fn, handle, datasize, data );
679     HeapFree( GetProcessHeap(), 0, fn );
680     return ret;
681 }
682
683 /*****************************************************************************
684  *
685  *   VerFindFile() [VER.8]
686  *   Determines where to install a file based on whether it locates another
687  *   version of the file in the system.  The values VerFindFile returns are
688  *   used in a subsequent call to the VerInstallFile function.
689  *
690  *   Revision history:
691  *      30-May-1997   Dave Cuthbert (dacut@ece.cmu.edu)
692  *         Reimplementation of VerFindFile from original stub.
693  *
694  ****************************************************************************/
695
696 DWORD WINAPI VerFindFile16(
697     UINT16 flags,
698     LPCSTR lpszFilename,
699     LPCSTR lpszWinDir,
700     LPCSTR lpszAppDir,
701     LPSTR lpszCurDir,
702     UINT16 *lpuCurDirLen,
703     LPSTR lpszDestDir,
704     UINT16 *lpuDestDirLen )
705 {
706     DWORD  retval;
707     char  curDir[256];
708     char  destDir[256];
709     unsigned int  curDirSizeReq;
710     unsigned int  destDirSizeReq;
711
712     retval = 0;
713
714     /* Print out debugging information */
715     dprintf_info(ver, "VerFindFile() called with parameters:\n"
716                 "\tflags = %x", flags);
717     if(flags & VFFF_ISSHAREDFILE)
718         dprintf_info(ver, " (VFFF_ISSHAREDFILE)\n");
719     else
720         dprintf_info(ver, "\n");
721
722     ver_dstring("\tlpszFilename = ", lpszFilename, "");
723     ver_dstring("\tlpszWinDir = ", lpszWinDir, "");
724     ver_dstring("\tlpszAppDir = ", lpszAppDir, "");
725
726     dprintf_info(ver, "\tlpszCurDir = %p\n", lpszCurDir);
727     if(lpuCurDirLen)
728         dprintf_info(ver, "\tlpuCurDirLen = %p (%u)\n",
729                     lpuCurDirLen, *lpuCurDirLen);
730     else
731         dprintf_info(ver, "\tlpuCurDirLen = (null)\n");
732
733     dprintf_info(ver, "\tlpszDestDir = %p\n", lpszDestDir);
734     if(lpuDestDirLen)
735         dprintf_info(ver, "\tlpuDestDirLen = %p (%u)\n",
736                     lpuDestDirLen, *lpuDestDirLen);
737
738     /* Figure out where the file should go; shared files default to the
739        system directory */
740
741     strcpy(curDir, "");
742     strcpy(destDir, "");
743
744     if(flags & VFFF_ISSHAREDFILE) {
745         GetSystemDirectory32A(destDir, 256);
746
747         /* Were we given a filename?  If so, try to find the file. */
748         if(lpszFilename) {
749             if(testFileExistence(destDir, lpszFilename)) {
750                 strcpy(curDir, destDir);
751
752                 if(!testFileExclusiveExistence(destDir, lpszFilename))
753                     retval |= VFF_FILEINUSE;
754             }
755             else if(lpszAppDir && testFileExistence(lpszAppDir,
756                                                     lpszFilename)) {
757                 strcpy(curDir, lpszAppDir);
758                 retval |= VFF_CURNEDEST;
759
760                 if(!testFileExclusiveExistence(lpszAppDir, lpszFilename))
761                     retval |= VFF_FILEINUSE;
762             }
763         }
764     }
765     else if(!(flags & VFFF_ISSHAREDFILE)) { /* not a shared file */
766         if(lpszAppDir) {
767             char  systemDir[256];
768             GetSystemDirectory32A(systemDir, 256);
769
770             strcpy(destDir, lpszAppDir);
771
772             if(lpszFilename) {
773                 if(testFileExistence(lpszAppDir, lpszFilename)) {
774                     strcpy(curDir, lpszAppDir);
775
776                     if(!testFileExclusiveExistence(lpszAppDir, lpszFilename))
777                         retval |= VFF_FILEINUSE;
778                 }
779                 else if(testFileExistence(systemDir, lpszFilename)) {
780                     strcpy(curDir, systemDir);
781                     retval |= VFF_CURNEDEST;
782
783                     if(!testFileExclusiveExistence(systemDir, lpszFilename))
784                         retval |= VFF_FILEINUSE;
785                 }
786             }
787         }
788     }
789
790     curDirSizeReq = strlen(curDir) + 1;
791     destDirSizeReq = strlen(destDir) + 1;
792
793
794
795     /* Make sure that the pointers to the size of the buffers are
796        valid; if not, do NOTHING with that buffer.  If that pointer
797        is valid, then make sure that the buffer pointer is valid, too! */
798
799     if(lpuDestDirLen && lpszDestDir) {
800         if(*lpuDestDirLen < destDirSizeReq) {
801             retval |= VFF_BUFFTOOSMALL;
802             strncpy(lpszDestDir, destDir, *lpuDestDirLen - 1);
803             lpszDestDir[*lpuDestDirLen - 1] = '\0';
804         }
805         else
806             strcpy(lpszDestDir, destDir);
807
808         *lpuDestDirLen = destDirSizeReq;
809     }
810     
811     if(lpuCurDirLen && lpszCurDir) {
812         if(*lpuCurDirLen < curDirSizeReq) {
813             retval |= VFF_BUFFTOOSMALL;
814             strncpy(lpszCurDir, curDir, *lpuCurDirLen - 1);
815             lpszCurDir[*lpuCurDirLen - 1] = '\0';
816         }
817         else
818             strcpy(lpszCurDir, curDir);
819
820         *lpuCurDirLen = curDirSizeReq;
821     }
822
823     dprintf_info(ver, "VerFindFile() ret = %lu (%s%s%s)\n", retval,
824                 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
825                 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
826                 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "");
827
828     ver_dstring("\t(Exit) lpszCurDir = ", lpszCurDir, "");
829     if(lpuCurDirLen)
830         dprintf_info(ver, "\t(Exit) lpuCurDirLen = %p (%u)\n",
831                     lpuCurDirLen, *lpuCurDirLen);
832     else
833         dprintf_info(ver, "\t(Exit) lpuCurDirLen = (null)\n");
834
835     ver_dstring("\t(Exit) lpszDestDir = ", lpszDestDir, "");
836     if(lpuDestDirLen)
837         dprintf_info(ver, "\t(Exit) lpuDestDirLen = %p (%u)\n",
838                     lpuDestDirLen, *lpuDestDirLen);
839
840     return retval;
841 }
842
843 /* VerFindFileA                                         [VERSION.5] */
844 DWORD WINAPI VerFindFile32A(
845         UINT32 flags,LPCSTR filename,LPCSTR windir,LPCSTR appdir,
846         LPSTR curdir,UINT32 *pcurdirlen,LPSTR destdir,UINT32 *pdestdirlen )
847 {
848     UINT16 curdirlen, destdirlen;
849     DWORD ret;
850     
851     curdirlen = (UINT16)*pcurdirlen;
852     destdirlen= (UINT16)*pdestdirlen;
853
854     ret = VerFindFile16(flags,filename,windir,appdir,
855                         curdir,&curdirlen,destdir,&destdirlen);
856     *pcurdirlen = curdirlen;
857     *pdestdirlen = destdirlen;
858     return ret;
859 }
860
861 /* VerFindFileW                                         [VERSION.6] */
862 DWORD WINAPI VerFindFile32W(
863         UINT32 flags,LPCWSTR filename,LPCWSTR windir,LPCWSTR appdir,
864         LPWSTR curdir,UINT32 *pcurdirlen,LPWSTR destdir,UINT32 *pdestdirlen )
865 {
866     UINT16 curdirlen, destdirlen;
867     LPSTR wfn,wwd,wad,wdd,wcd;
868     DWORD ret;
869
870     wfn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
871     wwd = HEAP_strdupWtoA( GetProcessHeap(), 0, windir );
872     wad = HEAP_strdupWtoA( GetProcessHeap(), 0, appdir );
873     wcd = HeapAlloc( GetProcessHeap(), 0, *pcurdirlen );
874     wdd = HeapAlloc( GetProcessHeap(), 0, *pdestdirlen );
875     ret = VerFindFile16(flags,wfn,wwd,wad,wcd,&curdirlen,wdd,&destdirlen);
876     lstrcpynAtoW(curdir,wcd,*pcurdirlen);
877     lstrcpynAtoW(destdir,wdd,*pdestdirlen);
878     *pcurdirlen = strlen(wcd);
879     *pdestdirlen = strlen(wdd);
880     HeapFree( GetProcessHeap(), 0, wfn );
881     HeapFree( GetProcessHeap(), 0, wwd );
882     HeapFree( GetProcessHeap(), 0, wad );
883     HeapFree( GetProcessHeap(), 0, wcd );
884     HeapFree( GetProcessHeap(), 0, wdd );
885     return ret;
886 }
887
888 /* VerInstallFile                                       [VER.9] */
889 DWORD WINAPI VerInstallFile16(
890         UINT16 flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
891         LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,UINT16 *tmpfilelen )
892 {
893     UINT32      filelen;
894     DWORD ret= VerInstallFile32A(flags,srcfilename,destfilename,srcdir,
895                                  destdir,curdir,tmpfile,&filelen);
896
897     *tmpfilelen = filelen;
898     return ret;
899 }
900
901 /* VerInstallFileA                              [VERSION.7] */
902 static LPBYTE
903 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
904     DWORD       alloclen;
905     LPBYTE      buf;
906     DWORD       ret;
907
908     alloclen = 1000;
909     buf= xmalloc(alloclen);
910     while (1) {
911         ret = GetFileVersionInfo32A(fn,0,alloclen,buf);
912         if (!ret) {
913             free(buf);
914             return 0;
915         }
916         if (alloclen<*(WORD*)buf) {
917             free(buf);
918             alloclen = *(WORD*)buf;
919             buf = xmalloc(alloclen);
920         } else {
921             *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
922             if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
923                 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
924             if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
925                 fprintf(stderr,"_fetch_versioninfo:bad VS_FIXEDFILEINFO signature 0x%08lx\n",(*vffi)->dwSignature);
926             return buf;
927         }
928     }
929 }
930
931 static DWORD
932 _error2vif(DWORD error) {
933     switch (error) {
934     case ERROR_ACCESS_DENIED:
935         return VIF_ACCESSVIOLATION;
936     case ERROR_SHARING_VIOLATION:
937         return VIF_SHARINGVIOLATION;
938     default:
939         return 0;
940     }
941 }
942
943 /* VerInstallFile32A
944  */
945
946 DWORD WINAPI VerInstallFile32A(
947         UINT32 flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
948         LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,UINT32 *tmpfilelen )
949 {
950     LPCSTR pdest;
951     char        destfn[260],tmpfn[260],srcfn[260];
952     HFILE32     hfsrc,hfdst;
953     DWORD       attr,ret,xret,tmplast;
954     LPBYTE      buf1,buf2;
955     OFSTRUCT    ofs;
956
957     fprintf(stddeb,"VerInstallFile(%x,%s,%s,%s,%s,%s,%p,%d)\n",
958             flags,srcfilename,destfilename,srcdir,destdir,curdir,tmpfile,*tmpfilelen
959     );
960     xret = 0;
961     sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
962     if (!destdir || !*destdir) pdest = srcdir;
963     else pdest = destdir;
964     sprintf(destfn,"%s\\%s",pdest,destfilename);
965     hfsrc=LZOpenFile32A(srcfn,&ofs,OF_READ);
966     if (hfsrc==HFILE_ERROR32)
967         return VIF_CANNOTREADSRC;
968     sprintf(tmpfn,"%s\\%s",pdest,destfilename);
969     tmplast=strlen(pdest)+1;
970     attr = GetFileAttributes32A(tmpfn);
971     if (attr!=-1) {
972         if (attr & FILE_ATTRIBUTE_READONLY) {
973             LZClose32(hfsrc);
974             return VIF_WRITEPROT;
975         }
976         /* FIXME: check if file currently in use and return VIF_FILEINUSE */
977     }
978     attr = -1;
979     if (flags & VIFF_FORCEINSTALL) {
980         if (tmpfile[0]) {
981             sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
982             tmplast = strlen(pdest)+1;
983             attr = GetFileAttributes32A(tmpfn);
984             /* if it exists, it has been copied by the call before.
985              * we jump over the copy part... 
986              */
987         }
988     }
989     if (attr == -1) {
990         char    *s;
991
992         GetTempFileName32A(pdest,"ver",0,tmpfn); /* should not fail ... */
993         s=strrchr(tmpfn,'\\');
994         if (s)
995             tmplast = s-tmpfn;
996         else
997             tmplast = 0;
998         hfdst = OpenFile32(tmpfn,&ofs,OF_CREATE);
999         if (hfdst == HFILE_ERROR32) {
1000             LZClose32(hfsrc);
1001             return VIF_CANNOTCREATE; /* | translated dos error */
1002         }
1003         ret = LZCopy32(hfsrc,hfdst);
1004         _lclose32(hfdst);
1005         if (((long) ret) < 0) {
1006             /* translate LZ errors into VIF_xxx */
1007             switch (ret) {
1008             case LZERROR_BADINHANDLE:
1009             case LZERROR_READ:
1010             case LZERROR_BADVALUE:
1011             case LZERROR_UNKNOWNALG:
1012                 ret = VIF_CANNOTREADSRC;
1013                 break;
1014             case LZERROR_BADOUTHANDLE:
1015             case LZERROR_WRITE:
1016                 ret = VIF_OUTOFMEMORY; /* FIXME: correct? */
1017                 break;
1018             case LZERROR_GLOBALLOC:
1019             case LZERROR_GLOBLOCK:
1020                 ret = VIF_OUTOFSPACE;
1021                 break;
1022             default: /* unknown error, should not happen */
1023                 ret = 0;
1024                 break;
1025             }
1026             if (ret) {
1027                 LZClose32(hfsrc);
1028                 return ret;
1029             }
1030         }
1031     }
1032     xret = 0;
1033     if (!(flags & VIFF_FORCEINSTALL)) {
1034         VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1035         buf1 = _fetch_versioninfo(destfn,&destvffi);
1036         if (buf1) {
1037             buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1038             if (buf2) {
1039                 char    *tbuf1,*tbuf2;
1040                 UINT32  len1,len2;
1041
1042                 len1=len2=40;
1043
1044                 /* compare file versions */
1045                 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1046                     ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1047                      (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1048                     )
1049                 )
1050                     xret |= VIF_MISMATCH|VIF_SRCOLD;
1051                 /* compare filetypes and filesubtypes */
1052                 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1053                     (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1054                 )
1055                     xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1056                 if (VerQueryValue32A(buf1,"\\VarFileInfo\\Translation",(LPVOID*)&tbuf1,&len1) &&
1057                     VerQueryValue32A(buf2,"\\VarFileInfo\\Translation",(LPVOID*)&tbuf2,&len2)
1058                 ) {
1059                     /* irgendwas mit tbuf1 und tbuf2 machen 
1060                      * generiert DIFFLANG|MISMATCH
1061                      */
1062                 }
1063                 free(buf2);
1064             } else
1065                 xret=VIF_MISMATCH|VIF_SRCOLD;
1066             free(buf1);
1067         }
1068     }
1069     if (xret) {
1070         if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1071             xret|=VIF_BUFTOSMALL;
1072             DeleteFile32A(tmpfn);
1073         } else {
1074             strcpy(tmpfile,tmpfn+tmplast);
1075             *tmpfilelen = strlen(tmpfn+tmplast)+1;
1076             xret|=VIF_TEMPFILE;
1077         }
1078     } else {
1079         if (-1!=GetFileAttributes32A(destfn))
1080             if (!DeleteFile32A(destfn)) {
1081                 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1082                 DeleteFile32A(tmpfn);
1083                 LZClose32(hfsrc);
1084                 return xret;
1085             }
1086         if ((!(flags & VIFF_DONTDELETEOLD))     && 
1087             curdir                              && 
1088             *curdir                             &&
1089             lstrcmpi32A(curdir,pdest)
1090         ) {
1091             char curfn[260];
1092
1093             sprintf(curfn,"%s\\%s",curdir,destfilename);
1094             if (-1!=GetFileAttributes32A(curfn)) {
1095                 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1096                 if (!DeleteFile32A(curfn))
1097                     xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1098             }
1099         }
1100         if (!MoveFile32A(tmpfn,destfn)) {
1101             xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1102             DeleteFile32A(tmpfn);
1103         }
1104     }
1105     LZClose32(hfsrc);
1106     return xret;
1107 }
1108
1109 /* VerInstallFileW                              [VERSION.8] */
1110 DWORD WINAPI VerInstallFile32W(
1111         UINT32 flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1112         LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,UINT32 *tmpfilelen )
1113 {
1114     LPSTR wsrcf,wsrcd,wdestf,wdestd,wtmpf,wcurd;
1115     DWORD ret;
1116
1117     wsrcf  = HEAP_strdupWtoA( GetProcessHeap(), 0, srcfilename );
1118     wsrcd  = HEAP_strdupWtoA( GetProcessHeap(), 0, srcdir );
1119     wdestf = HEAP_strdupWtoA( GetProcessHeap(), 0, destfilename );
1120     wdestd = HEAP_strdupWtoA( GetProcessHeap(), 0, destdir );
1121     wtmpf  = HEAP_strdupWtoA( GetProcessHeap(), 0, tmpfile );
1122     wcurd  = HEAP_strdupWtoA( GetProcessHeap(), 0, curdir );
1123     ret = VerInstallFile32A(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,tmpfilelen);
1124     if (!ret)
1125         lstrcpynAtoW(tmpfile,wtmpf,*tmpfilelen);
1126     HeapFree( GetProcessHeap(), 0, wsrcf );
1127     HeapFree( GetProcessHeap(), 0, wsrcd );
1128     HeapFree( GetProcessHeap(), 0, wdestf );
1129     HeapFree( GetProcessHeap(), 0, wdestd );
1130     HeapFree( GetProcessHeap(), 0, wtmpf );
1131     if (wcurd) 
1132         HeapFree( GetProcessHeap(), 0, wcurd );
1133     return ret;
1134 }
1135
1136
1137 struct dbA {
1138         WORD    nextoff;
1139         WORD    datalen;
1140 /* in memory structure... */
1141         char    name[1];        /* padded to dword alignment */
1142 /* .... 
1143         char    data[datalen];     padded to dword alignment
1144         BYTE    subdirdata[];      until nextoff
1145  */
1146 };
1147
1148 #define DATA_OFFSET_A(db) ((4+(strlen((db)->name)+4))&~3)
1149
1150 struct dbW {
1151         WORD    nextoff;
1152         WORD    datalen;
1153         WORD    btext;          /* type of data */
1154 /* in memory structure... */
1155         WCHAR   name[1];        /* padded to dword alignment */
1156 /* .... 
1157         WCHAR   data[datalen];     padded to dword alignment
1158         BYTE    subdirdata[];      until nextoff
1159  */
1160 };
1161
1162 /* WORD nextoffset;
1163  * WORD datalength;
1164  * WORD btype;
1165  * WCHAR szKey[]; (zero terminated)
1166  * PADDING (round up to nearest 32bit boundary)
1167  */
1168 #define DATA_OFFSET_W(db) ((2+2+2+((lstrlen32W((db)->name)+1)*2+3))&~3)
1169
1170 /* this one used for Win16 resources, which are always in ASCII format */
1171 static BYTE*
1172 _find_dataA(BYTE *block,LPCSTR str, int buff_remain) {
1173         char    *nextslash;
1174         int     substrlen, inc_size;
1175         struct  dbA     *db;
1176
1177         while (*str && *str=='\\')
1178                 str++;
1179         if (NULL!=(nextslash=strchr(str,'\\')))
1180                 substrlen=nextslash-str;
1181         else
1182                 substrlen=strlen(str);
1183         if (nextslash!=NULL) {
1184                 while (*nextslash && *nextslash=='\\')
1185                         nextslash++;
1186                 if (!*nextslash)
1187                         nextslash=NULL;
1188         } else if (*str == 0)
1189                 return NULL;
1190
1191
1192         while (1) {
1193                 db=(struct dbA*)block;
1194                 dprintf_info(ver,"db=%p,db->nextoff=%d,db->datalen=%d,db->name=%s\n",
1195                         db,db->nextoff,db->datalen,db->name
1196                 );
1197                 if ((!db->nextoff) || (buff_remain<=0)) /* no more entries ? */
1198                         return NULL;
1199
1200                 dprintf_info(ver,"comparing with %s\n",db->name);
1201                 if (!lstrncmpi32A(db->name,str,substrlen)) {
1202                         if (nextslash) {
1203                                 inc_size=DATA_OFFSET_A(db)+((db->datalen+3)&~3);
1204                                 return _find_dataA(block+inc_size,nextslash,
1205                                                         db->nextoff-inc_size);
1206                         } else
1207                                 return block;
1208                 }
1209                 inc_size         = ((db->nextoff+3)&~3);
1210                 block           += inc_size;
1211                 buff_remain     -= inc_size;
1212         }
1213 }
1214
1215 /* this one used for Win32 resources, which are always in UNICODE format */
1216 extern LPWSTR __cdecl CRTDLL_wcschr(LPCWSTR str,WCHAR xchar);
1217 static BYTE*
1218 _find_dataW(BYTE *block,LPCWSTR str, int buff_remain) {
1219         LPWSTR  nextslash;
1220         int     substrlen, inc_size;
1221         struct  dbW     *db;
1222
1223
1224         while (*str && *str=='\\')
1225                 str++;
1226         if (NULL!=(nextslash=CRTDLL_wcschr(str,'\\')))
1227                 substrlen=nextslash-str;
1228         else
1229                 substrlen=lstrlen32W(str);
1230         if (nextslash!=NULL) {
1231                 while (*nextslash && *nextslash=='\\')
1232                         nextslash++;
1233                 if (!*nextslash)
1234                         nextslash=NULL;
1235         } else if (*str == 0)
1236                 return NULL;
1237
1238
1239         while (1) {
1240                 char    *xs,*vs;
1241                 db=(struct dbW*)block;
1242                 xs= HEAP_strdupWtoA(GetProcessHeap(),0,db->name);
1243                 if (db->datalen) {
1244                         if (db->btext)
1245                                 vs = HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR*)((block+DATA_OFFSET_W(db))));
1246                         else
1247                                 vs = HEAP_strdupA(GetProcessHeap(),0,"not a string");
1248                 } else
1249                         vs = HEAP_strdupA(GetProcessHeap(),0,"no data");
1250
1251                 dprintf_info(ver,"db->nextoff=%d,db->name=%s,db->data=\"%s\"\n",
1252                         db->nextoff,xs,vs
1253                 );
1254                 HeapFree(GetProcessHeap(),0,vs);
1255                 HeapFree(GetProcessHeap(),0,xs);
1256                 if ((!db->nextoff) || (buff_remain<=0)) /* no more entries ? */
1257                         return NULL;
1258
1259                 if (!lstrncmpi32W(db->name,str,substrlen)) {
1260                         if (nextslash) {
1261                                 /* DATA_OFFSET_W(db) (padded to 32bit already)
1262                                  * DATA[datalength]
1263                                  * PADDING (round up to nearest 32bit boundary)
1264                                  * -->  next level structs
1265                                  */
1266                                 inc_size=DATA_OFFSET_W(db)+((db->datalen+3)&~3);
1267                                 return _find_dataW( block+inc_size ,nextslash,
1268                                                         db->nextoff-inc_size);
1269                         } else
1270                                 return block;
1271                 }
1272                 /* skip over this block, round up to nearest 32bit boundary */
1273                 inc_size        =  ((db->nextoff+3)&~3);
1274                 block           += inc_size;
1275                 buff_remain     -= inc_size;
1276         }
1277 }
1278
1279 /* VerQueryValue                        [VER.11] */
1280 /* take care, 'buffer' is NOT a SEGPTR, it just points to one */
1281 DWORD WINAPI VerQueryValue16(SEGPTR segblock,LPCSTR subblock,SEGPTR *buffer,
1282                              UINT16 *buflen)
1283 {
1284         LPSTR   s;
1285         BYTE    *block=PTR_SEG_TO_LIN(segblock),*b;
1286
1287         dprintf_info(ver,"VerQueryValue16(%p,%s,%p,%d)\n",
1288                 block,subblock,buffer,*buflen
1289         );
1290
1291         s=(char*)xmalloc(strlen("VS_VERSION_INFO\\")+strlen(subblock)+1);
1292         strcpy(s,"VS_VERSION_INFO\\");strcat(s,subblock);
1293         /* check for UNICODE version */
1294         if (    (*(DWORD*)(block+0x14) != VS_FFI_SIGNATURE) && 
1295                 (*(DWORD*)(block+0x28) == VS_FFI_SIGNATURE)
1296         ) {
1297                 struct  dbW     *db;
1298                 LPWSTR  wstr;
1299                 LPSTR   xs;
1300
1301                 wstr = HEAP_strdupAtoW(GetProcessHeap(),0,s);
1302                 b=_find_dataW(block,wstr,*(WORD*)block);
1303                 HeapFree(GetProcessHeap(),0,wstr);
1304                 if (!b) {
1305                         fprintf(stderr,"key %s not found in versionresource.\n",s);
1306                         *buflen=0;
1307                         free (s);
1308                         return 0;
1309                 }
1310                 db=(struct dbW*)b;
1311                 b       = b+DATA_OFFSET_W(db);
1312                 *buflen = db->datalen;
1313                 if (db->btext) {
1314                     xs = HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR*)b);
1315                     dprintf_info(ver,"->%s\n",xs);
1316                     HeapFree(GetProcessHeap(),0,xs);
1317                 } else
1318                     dprintf_info(ver,"->%p\n",b);
1319         } else {
1320                 struct  dbA     *db;
1321                 b=_find_dataA(block,s,*(WORD*)block);
1322                 if (!b) {
1323                         fprintf(stderr,"key %s not found in versionresource.\n",s);
1324                         *buflen=0;
1325                         free (s);
1326                         return 0;
1327                 }
1328                 db=(struct dbA*)b;
1329                 b       = b+DATA_OFFSET_A(db);
1330                 *buflen = db->datalen;
1331                 /* the string is only printable, if it is below \\StringFileInfo*/
1332                 if (!lstrncmpi32A("VS_VERSION_INFO\\StringFileInfo\\",s,strlen("VS_VERSION_INFO\\StringFileInfo\\")))
1333                     dprintf_info(ver,"  -> %s=%s\n",subblock,b);
1334                 else
1335                     dprintf_info(ver,"  -> %s=%p\n",subblock,b);
1336         }
1337         *buffer = (b-block)+segblock;
1338         free(s);
1339         return 1;
1340 }
1341
1342 DWORD WINAPI VerQueryValue32A(LPVOID vblock,LPCSTR subblock,
1343                               LPVOID *vbuffer,UINT32 *buflen)
1344 {
1345         BYTE    *b,*block=(LPBYTE)vblock,**buffer=(LPBYTE*)vbuffer;
1346         LPSTR   s;
1347
1348         dprintf_info(ver,"VerQueryValue32A(%p,%s,%p,%d)\n",
1349                 block,subblock,buffer,*buflen
1350         );
1351
1352         s=(char*)xmalloc(strlen("VS_VERSION_INFO\\")+strlen(subblock)+1);
1353         strcpy(s,"VS_VERSION_INFO\\");strcat(s,subblock);
1354
1355         /* check for UNICODE version */
1356         if (    (*(DWORD*)(block+0x14) != VS_FFI_SIGNATURE) && 
1357                 (*(DWORD*)(block+0x28) == VS_FFI_SIGNATURE)
1358         ) {
1359                 LPWSTR  wstr;
1360                 LPSTR   xs;
1361                 struct  dbW     *db;
1362
1363                 wstr = HEAP_strdupAtoW(GetProcessHeap(),0,s);
1364                 b=_find_dataW(block,wstr,*(WORD*)block);
1365                 HeapFree(GetProcessHeap(),0,wstr);
1366                 if (!b) {
1367                         fprintf(stderr,"key %s not found in versionresource.\n",s);
1368                         *buflen=0;
1369                         free (s);
1370                         return 0;
1371                 }
1372                 db      = (struct dbW*)b;
1373                 *buflen = db->datalen;
1374                 b       = b+DATA_OFFSET_W(db);
1375                 if (db->btext) {
1376                     xs = HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR*)b);
1377                     dprintf_info(ver,"->%s\n",xs);
1378                     HeapFree(GetProcessHeap(),0,xs);
1379                 } else
1380                     dprintf_info(ver,"->%p\n",b);
1381                 /* This is a leak.  */
1382                 b = HEAP_strdupWtoA(GetProcessHeap(),0,(WCHAR*)b);
1383         } else {
1384                 struct  dbA     *db;
1385                 b=_find_dataA(block,s,*(WORD*)block);
1386                 if (!b) {
1387                         fprintf(stderr,"key %s not found in versionresource.\n",subblock);
1388                         *buflen=0;
1389                         free (s);
1390                         return 0;
1391                 }
1392                 db=(struct dbA*)b;
1393                 *buflen = db->datalen;
1394                 b       = b+DATA_OFFSET_A(db);
1395
1396                 /* the string is only printable, if it is below \\StringFileInfo*/
1397                 if (!lstrncmpi32A("VS_VERSION_INFO\\StringFileInfo\\",s,strlen("VS_VERSION_INFO\\StringFileInfo\\")))
1398                     dprintf_info(ver,"  -> %s=%s\n",subblock,b);
1399                 else
1400                     dprintf_info(ver,"  -> %s=%p\n",subblock,b);
1401         }
1402         *buffer = b;
1403         free(s);
1404         return 1;
1405 }
1406
1407 DWORD WINAPI VerQueryValue32W(LPVOID vblock,LPCWSTR subblock,LPVOID *vbuffer,
1408                               UINT32 *buflen)
1409 {
1410         LPSTR           sb;
1411         DWORD           ret;
1412
1413         sb = HEAP_strdupWtoA( GetProcessHeap(), 0, subblock );
1414         ret = VerQueryValue32A(vblock,sb,vbuffer,buflen);
1415         HeapFree( GetProcessHeap(), 0, sb );
1416         return 1;
1417 }
1418 /* 20 GETFILEVERSIONINFORAW */