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