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