msvcp90: Added locale::id class implementation.
[wine] / dlls / version / version.c
1 /*
2  * Implementation of VERSION.DLL
3  *
4  * Copyright 1996,1997 Marcus Meissner
5  * Copyright 1997 David Cuthbert
6  * Copyright 1999 Ulrich Weigand
7  * Copyright 2005 Paul Vriens
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  *
23  */
24 #include "config.h"
25
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 #include <sys/types.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35
36 #define NONAMELESSUNION
37 #define NONAMELESSSTRUCT
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winver.h"
41 #include "winuser.h"
42 #include "winnls.h"
43 #include "winternl.h"
44 #include "lzexpand.h"
45 #include "wine/unicode.h"
46 #include "winerror.h"
47 #include "wine/debug.h"
48
49
50 WINE_DEFAULT_DEBUG_CHANNEL(ver);
51
52 typedef struct
53 {
54     WORD offset;
55     WORD length;
56     WORD flags;
57     WORD id;
58     WORD handle;
59     WORD usage;
60 } NE_NAMEINFO;
61
62 typedef struct
63 {
64     WORD  type_id;
65     WORD  count;
66     DWORD resloader;
67 } NE_TYPEINFO;
68
69 /**********************************************************************
70  *  find_entry_by_id
71  *
72  * Find an entry by id in a resource directory
73  * Copied from loader/pe_resource.c
74  */
75 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
76                                                          WORD id, const void *root )
77 {
78     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
79     int min, max, pos;
80
81     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
82     min = dir->NumberOfNamedEntries;
83     max = min + dir->NumberOfIdEntries - 1;
84     while (min <= max)
85     {
86         pos = (min + max) / 2;
87         if (entry[pos].u1.s2.Id == id)
88             return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].u2.s3.OffsetToDirectory);
89         if (entry[pos].u1.s2.Id > id) max = pos - 1;
90         else min = pos + 1;
91     }
92     return NULL;
93 }
94
95
96 /**********************************************************************
97  *  find_entry_default
98  *
99  * Find a default entry in a resource directory
100  * Copied from loader/pe_resource.c
101  */
102 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
103                                                            const void *root )
104 {
105     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
106
107     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
108     return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry->u2.s3.OffsetToDirectory);
109 }
110
111
112 /***********************************************************************
113  *           read_xx_header         [internal]
114  */
115 static int read_xx_header( HFILE lzfd )
116 {
117     IMAGE_DOS_HEADER mzh;
118     char magic[3];
119
120     LZSeek( lzfd, 0, SEEK_SET );
121     if ( sizeof(mzh) != LZRead( lzfd, (LPSTR)&mzh, sizeof(mzh) ) )
122         return 0;
123     if ( mzh.e_magic != IMAGE_DOS_SIGNATURE )
124     {
125         if (!memcmp( &mzh, "\177ELF", 4 )) return 1;  /* ELF */
126         if (*(UINT *)&mzh == 0xfeedface || *(UINT *)&mzh == 0xcefaedfe) return 1;  /* Mach-O */
127         return 0;
128     }
129
130     LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
131     if ( 2 != LZRead( lzfd, magic, 2 ) )
132         return 0;
133
134     LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
135
136     if ( magic[0] == 'N' && magic[1] == 'E' )
137         return IMAGE_OS2_SIGNATURE;
138     if ( magic[0] == 'P' && magic[1] == 'E' )
139         return IMAGE_NT_SIGNATURE;
140
141     magic[2] = '\0';
142     WARN("Can't handle %s files.\n", magic );
143     return 0;
144 }
145
146 /***********************************************************************
147  *           find_ne_resource         [internal]
148  */
149 static BOOL find_ne_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
150 {
151     const WORD typeid = VS_FILE_INFO | 0x8000;
152     const WORD resid = VS_VERSION_INFO | 0x8000;
153     IMAGE_OS2_HEADER nehd;
154     NE_TYPEINFO *typeInfo;
155     NE_NAMEINFO *nameInfo;
156     DWORD nehdoffset;
157     LPBYTE resTab;
158     DWORD resTabSize;
159     int count;
160
161     /* Read in NE header */
162     nehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
163     if ( sizeof(nehd) != LZRead( lzfd, (LPSTR)&nehd, sizeof(nehd) ) ) return 0;
164
165     resTabSize = nehd.ne_restab - nehd.ne_rsrctab;
166     if ( !resTabSize )
167     {
168         TRACE("No resources in NE dll\n" );
169         return FALSE;
170     }
171
172     /* Read in resource table */
173     resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize );
174     if ( !resTab ) return FALSE;
175
176     LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET );
177     if ( resTabSize != LZRead( lzfd, (char*)resTab, resTabSize ) )
178     {
179         HeapFree( GetProcessHeap(), 0, resTab );
180         return FALSE;
181     }
182
183     /* Find resource */
184     typeInfo = (NE_TYPEINFO *)(resTab + 2);
185     while (typeInfo->type_id)
186     {
187         if (typeInfo->type_id == typeid) goto found_type;
188         typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
189                                    typeInfo->count * sizeof(NE_NAMEINFO));
190     }
191     TRACE("No typeid entry found\n" );
192     HeapFree( GetProcessHeap(), 0, resTab );
193     return FALSE;
194
195  found_type:
196     nameInfo = (NE_NAMEINFO *)(typeInfo + 1);
197
198     for (count = typeInfo->count; count > 0; count--, nameInfo++)
199         if (nameInfo->id == resid) goto found_name;
200
201     TRACE("No resid entry found\n" );
202     HeapFree( GetProcessHeap(), 0, resTab );
203     return FALSE;
204
205  found_name:
206     /* Return resource data */
207     if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab;
208     if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab;
209
210     HeapFree( GetProcessHeap(), 0, resTab );
211     return TRUE;
212 }
213
214 /***********************************************************************
215  *           find_pe_resource         [internal]
216  */
217 static BOOL find_pe_resource( HFILE lzfd, DWORD *resLen, DWORD *resOff )
218 {
219     union
220     {
221         IMAGE_NT_HEADERS32 nt32;
222         IMAGE_NT_HEADERS64 nt64;
223     } pehd;
224     DWORD pehdoffset;
225     PIMAGE_DATA_DIRECTORY resDataDir;
226     PIMAGE_SECTION_HEADER sections;
227     LPBYTE resSection;
228     DWORD section_size, data_size;
229     const void *resDir;
230     const IMAGE_RESOURCE_DIRECTORY *resPtr;
231     const IMAGE_RESOURCE_DATA_ENTRY *resData;
232     int i, len, nSections;
233     BOOL ret = FALSE;
234
235     /* Read in PE header */
236     pehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
237     len = LZRead( lzfd, (LPSTR)&pehd, sizeof(pehd) );
238     if (len < sizeof(pehd.nt32.FileHeader)) return 0;
239     if (len < sizeof(pehd)) memset( (char *)&pehd + len, 0, sizeof(pehd) - len );
240
241     switch (pehd.nt32.OptionalHeader.Magic)
242     {
243     case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
244         resDataDir = pehd.nt32.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
245         break;
246     case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
247         resDataDir = pehd.nt64.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
248         break;
249     default:
250         return 0;
251     }
252
253     if ( !resDataDir->Size )
254     {
255         TRACE("No resources in PE dll\n" );
256         return FALSE;
257     }
258
259     /* Read in section table */
260     nSections = pehd.nt32.FileHeader.NumberOfSections;
261     sections = HeapAlloc( GetProcessHeap(), 0,
262                           nSections * sizeof(IMAGE_SECTION_HEADER) );
263     if ( !sections ) return FALSE;
264
265     len = FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) + pehd.nt32.FileHeader.SizeOfOptionalHeader;
266     LZSeek( lzfd, pehdoffset + len, SEEK_SET );
267
268     if ( nSections * sizeof(IMAGE_SECTION_HEADER) !=
269          LZRead( lzfd, (LPSTR)sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) )
270     {
271         HeapFree( GetProcessHeap(), 0, sections );
272         return FALSE;
273     }
274
275     /* Find resource section */
276     for ( i = 0; i < nSections; i++ )
277         if (    resDataDir->VirtualAddress >= sections[i].VirtualAddress
278              && resDataDir->VirtualAddress <  sections[i].VirtualAddress +
279                                               sections[i].SizeOfRawData )
280             break;
281
282     if ( i == nSections )
283     {
284         HeapFree( GetProcessHeap(), 0, sections );
285         TRACE("Couldn't find resource section\n" );
286         return FALSE;
287     }
288
289     /* Read in resource section */
290     data_size = sections[i].SizeOfRawData;
291     section_size = max( data_size, sections[i].Misc.VirtualSize );
292     resSection = HeapAlloc( GetProcessHeap(), 0, section_size );
293     if ( !resSection )
294     {
295         HeapFree( GetProcessHeap(), 0, sections );
296         return FALSE;
297     }
298
299     LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET );
300     if (data_size != LZRead( lzfd, (char*)resSection, data_size )) goto done;
301     if (data_size < section_size) memset( (char *)resSection + data_size, 0, section_size - data_size );
302
303     /* Find resource */
304     resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress);
305
306     resPtr = resDir;
307     resPtr = find_entry_by_id( resPtr, VS_FILE_INFO, resDir );
308     if ( !resPtr )
309     {
310         TRACE("No typeid entry found\n" );
311         goto done;
312     }
313     resPtr = find_entry_by_id( resPtr, VS_VERSION_INFO, resDir );
314     if ( !resPtr )
315     {
316         TRACE("No resid entry found\n" );
317         goto done;
318     }
319     resPtr = find_entry_default( resPtr, resDir );
320     if ( !resPtr )
321     {
322         TRACE("No default language entry found\n" );
323         goto done;
324     }
325
326     /* Find resource data section */
327     resData = (const IMAGE_RESOURCE_DATA_ENTRY*)resPtr;
328     for ( i = 0; i < nSections; i++ )
329         if (    resData->OffsetToData >= sections[i].VirtualAddress
330              && resData->OffsetToData <  sections[i].VirtualAddress +
331                                          sections[i].SizeOfRawData )
332             break;
333
334     if ( i == nSections )
335     {
336         TRACE("Couldn't find resource data section\n" );
337         goto done;
338     }
339
340     /* Return resource data */
341     if ( resLen ) *resLen = resData->Size;
342     if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress
343                             + sections[i].PointerToRawData;
344     ret = TRUE;
345
346  done:
347     HeapFree( GetProcessHeap(), 0, resSection );
348     HeapFree( GetProcessHeap(), 0, sections );
349     return ret;
350 }
351
352
353 /***********************************************************************
354  *           find_version_resource         [internal]
355  */
356 static DWORD find_version_resource( HFILE lzfd, DWORD *reslen, DWORD *offset )
357 {
358     DWORD magic = read_xx_header( lzfd );
359
360     switch (magic)
361     {
362     case IMAGE_OS2_SIGNATURE:
363         if (!find_ne_resource( lzfd, reslen, offset )) magic = 0;
364         break;
365     case IMAGE_NT_SIGNATURE:
366         if (!find_pe_resource( lzfd, reslen, offset )) magic = 0;
367         break;
368     }
369     return magic;
370 }
371
372 /******************************************************************************
373  *
374  *   This function will print via standard TRACE, debug info regarding
375  *   the file info structure vffi.
376  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
377  *      Added this function to clean up the code.
378  *
379  *****************************************************************************/
380 static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi)
381 {
382     BOOL    versioned_printer = FALSE;
383
384     if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
385     {
386         if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
387             /* this is documented for newer w2k Drivers and up */
388             versioned_printer = TRUE;
389         else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
390                  (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
391                  (vffi->dwFileVersionMS > 0) &&
392                  (vffi->dwFileVersionMS <= 3) )
393             /* found this on NT 3.51, NT4.0 and old w2k Drivers */
394             versioned_printer = TRUE;
395     }
396
397     TRACE("structversion=%u.%u, ",
398             HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
399     if(versioned_printer)
400     {
401         WORD mode = LOWORD(vffi->dwFileVersionMS);
402         WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
403         TRACE("fileversion=%u.%u.%u.%u (%s.major.minor.release), ",
404             (vffi->dwFileVersionMS),
405             HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
406             (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
407     }
408     else
409     {
410         TRACE("fileversion=%u.%u.%u.%u, ",
411             HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
412             HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
413     }
414     TRACE("productversion=%u.%u.%u.%u\n",
415           HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
416           HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
417
418     TRACE("flagmask=0x%x, flags=0x%x %s%s%s%s%s%s\n",
419           vffi->dwFileFlagsMask, vffi->dwFileFlags,
420           (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
421           (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
422           (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
423           (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
424           (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
425           (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
426
427     TRACE("(");
428
429     TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
430
431     switch (vffi->dwFileOS&0xFFFF0000)
432     {
433     case VOS_DOS:TRACE("DOS,");break;
434     case VOS_OS216:TRACE("OS/2-16,");break;
435     case VOS_OS232:TRACE("OS/2-32,");break;
436     case VOS_NT:TRACE("NT,");break;
437     case VOS_UNKNOWN:
438     default:
439         TRACE("UNKNOWN(0x%x),",vffi->dwFileOS&0xFFFF0000);break;
440     }
441
442     switch (LOWORD(vffi->dwFileOS))
443     {
444     case VOS__BASE:TRACE("BASE");break;
445     case VOS__WINDOWS16:TRACE("WIN16");break;
446     case VOS__WINDOWS32:TRACE("WIN32");break;
447     case VOS__PM16:TRACE("PM16");break;
448     case VOS__PM32:TRACE("PM32");break;
449     default:
450         TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
451     }
452
453     TRACE(")\n");
454
455     switch (vffi->dwFileType)
456     {
457     case VFT_APP:TRACE("filetype=APP");break;
458     case VFT_DLL:
459         TRACE("filetype=DLL");
460         if(vffi->dwFileSubtype != 0)
461         {
462             if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver  */
463                 TRACE(",PRINTER");
464             TRACE(" (subtype=0x%x)", vffi->dwFileSubtype);
465         }
466         break;
467     case VFT_DRV:
468         TRACE("filetype=DRV,");
469         switch(vffi->dwFileSubtype)
470         {
471         case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
472         case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
473         case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
474         case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
475         case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
476         case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
477         case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
478         case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
479         case VFT2_DRV_SOUND:TRACE("SOUND");break;
480         case VFT2_DRV_COMM:TRACE("COMM");break;
481         case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
482         case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
483         case VFT2_UNKNOWN:
484         default:
485             TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
486         }
487         break;
488     case VFT_FONT:
489         TRACE("filetype=FONT,");
490         switch (vffi->dwFileSubtype)
491         {
492         case VFT2_FONT_RASTER:TRACE("RASTER");break;
493         case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
494         case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
495         default:TRACE("UNKNOWN(0x%x)",vffi->dwFileSubtype);break;
496         }
497         break;
498     case VFT_VXD:TRACE("filetype=VXD");break;
499     case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
500     case VFT_UNKNOWN:
501     default:
502         TRACE("filetype=Unknown(0x%x)",vffi->dwFileType);break;
503     }
504
505     TRACE("\n");
506     TRACE("filedate=0x%x.0x%x\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
507 }
508
509 /***********************************************************************
510  * Version Info Structure
511  */
512
513 typedef struct
514 {
515     WORD  wLength;
516     WORD  wValueLength;
517     CHAR  szKey[1];
518 #if 0   /* variable length structure */
519     /* DWORD aligned */
520     BYTE  Value[];
521     /* DWORD aligned */
522     VS_VERSION_INFO_STRUCT16 Children[];
523 #endif
524 } VS_VERSION_INFO_STRUCT16;
525
526 typedef struct
527 {
528     WORD  wLength;
529     WORD  wValueLength;
530     WORD  wType;
531     WCHAR szKey[1];
532 #if 0   /* variable length structure */
533     /* DWORD aligned */
534     BYTE  Value[];
535     /* DWORD aligned */
536     VS_VERSION_INFO_STRUCT32 Children[];
537 #endif
538 } VS_VERSION_INFO_STRUCT32;
539
540 #define VersionInfoIs16( ver ) \
541     ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
542
543 #define DWORD_ALIGN( base, ptr ) \
544     ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
545
546 #define VersionInfo16_Value( ver )  \
547     DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
548 #define VersionInfo32_Value( ver )  \
549     DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
550
551 #define VersionInfo16_Children( ver )  \
552     (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
553                            ( ( (ver)->wValueLength + 3 ) & ~3 ) )
554 #define VersionInfo32_Children( ver )  \
555     (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
556                            ( ( (ver)->wValueLength * \
557                                ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
558
559 #define VersionInfo16_Next( ver ) \
560     (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
561 #define VersionInfo32_Next( ver ) \
562     (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
563
564
565 /***********************************************************************
566  *           GetFileVersionInfoSizeW         [VERSION.@]
567  */
568 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
569 {
570     DWORD len, offset, magic = 1;
571     HFILE lzfd;
572     HMODULE hModule;
573     OFSTRUCT ofs;
574
575     TRACE("(%s,%p)\n", debugstr_w(filename), handle );
576
577     if (handle) *handle = 0;
578
579     if (!filename)
580     {
581         SetLastError(ERROR_INVALID_PARAMETER);
582         return 0;
583     }
584     if (!*filename)
585     {
586         SetLastError(ERROR_BAD_PATHNAME);
587         return 0;
588     }
589
590     if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
591     {
592         magic = find_version_resource( lzfd, &len, &offset );
593         LZClose( lzfd );
594     }
595
596     if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
597     {
598         HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
599                                      MAKEINTRESOURCEW(VS_FILE_INFO) );
600         if (hRsrc)
601         {
602             magic = IMAGE_NT_SIGNATURE;
603             len = SizeofResource( hModule, hRsrc );
604         }
605         FreeLibrary( hModule );
606     }
607
608     switch (magic)
609     {
610     case IMAGE_OS2_SIGNATURE:
611         /* We have a 16bit resource.
612          *
613          * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
614          *
615          * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
616          *
617          * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
618          * info->wLength should be the same as len. Currently it isn't but that
619          * doesn't seem to be a problem (len is bigger than info->wLength).
620          */
621         SetLastError(0);
622         return (len - sizeof(VS_FIXEDFILEINFO)) * 4;
623
624     case IMAGE_NT_SIGNATURE:
625         /* We have a 32bit resource.
626          *
627          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
628          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
629          */
630         SetLastError(0);
631         return (len * 2) + 4;
632
633     default:
634         SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
635         return 0;
636     }
637 }
638
639 /***********************************************************************
640  *           GetFileVersionInfoSizeA         [VERSION.@]
641  */
642 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
643 {
644     UNICODE_STRING filenameW;
645     DWORD retval;
646
647     TRACE("(%s,%p)\n", debugstr_a(filename), handle );
648
649     if(filename)
650         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
651     else
652         filenameW.Buffer = NULL;
653
654     retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle);
655
656     RtlFreeUnicodeString(&filenameW);
657
658     return retval;
659 }
660
661 /***********************************************************************
662  *           GetFileVersionInfoW             [VERSION.@]
663  */
664 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle,
665                                     DWORD datasize, LPVOID data )
666 {
667     static const char signature[4] = "FE2X";
668     DWORD len, offset, magic = 1;
669     HFILE lzfd;
670     OFSTRUCT ofs;
671     HMODULE hModule;
672     VS_VERSION_INFO_STRUCT32* vvis = data;
673
674     TRACE("(%s,%d,size=%d,data=%p)\n",
675                 debugstr_w(filename), handle, datasize, data );
676
677     if (!data)
678     {
679         SetLastError(ERROR_INVALID_DATA);
680         return FALSE;
681     }
682
683     if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
684     {
685         if ((magic = find_version_resource( lzfd, &len, &offset )) > 1)
686         {
687             LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
688             len = LZRead( lzfd, data, min( len, datasize ) );
689         }
690         LZClose( lzfd );
691     }
692
693     if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
694     {
695         HRSRC hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
696                                      MAKEINTRESOURCEW(VS_FILE_INFO) );
697         if (hRsrc)
698         {
699             HGLOBAL hMem = LoadResource( hModule, hRsrc );
700             magic = IMAGE_NT_SIGNATURE;
701             len = min( SizeofResource(hModule, hRsrc), datasize );
702             memcpy( data, LockResource( hMem ), len );
703             FreeResource( hMem );
704         }
705         FreeLibrary( hModule );
706     }
707
708     switch (magic)
709     {
710     case IMAGE_OS2_SIGNATURE:
711         /* We have a 16bit resource. */
712         if (TRACE_ON(ver))
713             print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ));
714         SetLastError(0);
715         return TRUE;
716
717     case IMAGE_NT_SIGNATURE:
718         /* We have a 32bit resource.
719          *
720          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
721          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
722          */
723         len = vvis->wLength + sizeof(signature);
724         if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) );
725         if (TRACE_ON(ver))
726             print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis ));
727         SetLastError(0);
728         return TRUE;
729
730     default:
731         SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
732         return FALSE;
733     }
734 }
735
736 /***********************************************************************
737  *           GetFileVersionInfoA             [VERSION.@]
738  */
739 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle,
740                                     DWORD datasize, LPVOID data )
741 {
742     UNICODE_STRING filenameW;
743     BOOL retval;
744
745     TRACE("(%s,%d,size=%d,data=%p)\n",
746                 debugstr_a(filename), handle, datasize, data );
747
748     if(filename)
749         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
750     else
751         filenameW.Buffer = NULL;
752
753     retval = GetFileVersionInfoW(filenameW.Buffer, handle, datasize, data);
754
755     RtlFreeUnicodeString(&filenameW);
756
757     return retval;
758 }
759
760 /***********************************************************************
761  *           VersionInfo16_FindChild             [internal]
762  */
763 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
764                                             LPCSTR szKey, UINT cbKey )
765 {
766     const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
767
768     while ((char *)child < (char *)info + info->wLength )
769     {
770         if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
771             return child;
772
773         if (!(child->wLength)) return NULL;
774         child = VersionInfo16_Next( child );
775     }
776
777     return NULL;
778 }
779
780 /***********************************************************************
781  *           VersionInfo32_FindChild             [internal]
782  */
783 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
784                                             LPCWSTR szKey, UINT cbKey )
785 {
786     const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
787
788     while ((char *)child < (char *)info + info->wLength )
789     {
790         if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
791             return child;
792
793         if (!(child->wLength)) return NULL;
794         child = VersionInfo32_Next( child );
795     }
796
797     return NULL;
798 }
799
800 /***********************************************************************
801  *           VersionInfo16_QueryValue              [internal]
802  *
803  *    Gets a value from a 16-bit NE resource
804  */
805 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
806                                LPVOID *lplpBuffer, UINT *puLen )
807 {
808     while ( *lpSubBlock )
809     {
810         /* Find next path component */
811         LPCSTR lpNextSlash;
812         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
813             if ( *lpNextSlash == '\\' )
814                 break;
815
816         /* Skip empty components */
817         if ( lpNextSlash == lpSubBlock )
818         {
819             lpSubBlock++;
820             continue;
821         }
822
823         /* We have a non-empty component: search info for key */
824         info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
825         if ( !info )
826         {
827             if (puLen) *puLen = 0 ;
828             SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
829             return FALSE;
830         }
831
832         /* Skip path component */
833         lpSubBlock = lpNextSlash;
834     }
835
836     /* Return value */
837     *lplpBuffer = VersionInfo16_Value( info );
838     if (puLen)
839         *puLen = info->wValueLength;
840
841     return TRUE;
842 }
843
844 /***********************************************************************
845  *           VersionInfo32_QueryValue              [internal]
846  *
847  *    Gets a value from a 32-bit PE resource
848  */
849 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
850                                LPVOID *lplpBuffer, UINT *puLen )
851 {
852     TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
853
854     while ( *lpSubBlock )
855     {
856         /* Find next path component */
857         LPCWSTR lpNextSlash;
858         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
859             if ( *lpNextSlash == '\\' )
860                 break;
861
862         /* Skip empty components */
863         if ( lpNextSlash == lpSubBlock )
864         {
865             lpSubBlock++;
866             continue;
867         }
868
869         /* We have a non-empty component: search info for key */
870         info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
871         if ( !info )
872         {
873             if (puLen) *puLen = 0 ;
874             SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
875             return FALSE;
876         }
877
878         /* Skip path component */
879         lpSubBlock = lpNextSlash;
880     }
881
882     /* Return value */
883     *lplpBuffer = VersionInfo32_Value( info );
884     if (puLen)
885         *puLen = info->wValueLength;
886
887     return TRUE;
888 }
889
890 /***********************************************************************
891  *           VerQueryValueA              [VERSION.@]
892  */
893 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
894                                LPVOID *lplpBuffer, PUINT puLen )
895 {
896     static const char rootA[] = "\\";
897     static const char varfileinfoA[] = "\\VarFileInfo\\Translation";
898     const VS_VERSION_INFO_STRUCT16 *info = pBlock;
899
900     TRACE("(%p,%s,%p,%p)\n",
901                 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
902
903      if (!pBlock)
904         return FALSE;
905
906     if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
907         lpSubBlock = rootA;
908
909     if ( !VersionInfoIs16( info ) )
910     {
911         BOOL ret;
912         INT len;
913         LPWSTR lpSubBlockW;
914
915         len  = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
916         lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
917
918         if (!lpSubBlockW)
919             return FALSE;
920
921         MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
922
923         ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen);
924
925         HeapFree(GetProcessHeap(), 0, lpSubBlockW);
926
927         if (ret && strcasecmp( lpSubBlock, rootA ) && strcasecmp( lpSubBlock, varfileinfoA ))
928         {
929             /* Set lpBuffer so it points to the 'empty' area where we store
930              * the converted strings
931              */
932             LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
933             DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
934
935             len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, -1,
936                                       lpBufferA + pos, info->wLength - pos, NULL, NULL);
937             *lplpBuffer = lpBufferA + pos;
938             *puLen = len;
939         }
940         return ret;
941     }
942
943     return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
944 }
945
946 /***********************************************************************
947  *           VerQueryValueW              [VERSION.@]
948  */
949 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
950                                LPVOID *lplpBuffer, PUINT puLen )
951 {
952     static const WCHAR nullW[] = { 0 };
953     static const WCHAR rootW[] = { '\\', 0 };
954     static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
955                                           '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
956
957     const VS_VERSION_INFO_STRUCT32 *info = pBlock;
958
959     TRACE("(%p,%s,%p,%p)\n",
960                 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
961
962     if (!pBlock)
963         return FALSE;
964
965     if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
966         lpSubBlock = rootW;
967
968     if ( VersionInfoIs16( info ) )
969     {
970         BOOL ret;
971         int len;
972         LPSTR lpSubBlockA;
973
974         len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
975         lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
976
977         if (!lpSubBlockA)
978             return FALSE;
979
980         WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
981
982         ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
983
984         HeapFree(GetProcessHeap(), 0, lpSubBlockA);
985
986         if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
987         {
988             /* Set lpBuffer so it points to the 'empty' area where we store
989              * the converted strings
990              */
991             LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
992             DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
993             DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
994
995             len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
996                                       lpBufferW + pos, max/sizeof(WCHAR) - pos );
997             *lplpBuffer = lpBufferW + pos;
998             *puLen = len;
999         }
1000         return ret;
1001     }
1002
1003     return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1004 }
1005
1006
1007 /******************************************************************************
1008  *   testFileExistenceA
1009  *
1010  *   Tests whether a given path/file combination exists.  If the file does
1011  *   not exist, the return value is zero.  If it does exist, the return
1012  *   value is non-zero.
1013  *
1014  *   Revision history
1015  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1016  *         Original implementation
1017  *
1018  */
1019 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1020 {
1021     char  filename[1024];
1022     int  filenamelen;
1023     OFSTRUCT  fileinfo;
1024
1025     fileinfo.cBytes = sizeof(OFSTRUCT);
1026
1027     strcpy(filename, path);
1028     filenamelen = strlen(filename);
1029
1030     /* Add a trailing \ if necessary */
1031     if(filenamelen) {
1032         if(filename[filenamelen - 1] != '\\')
1033             strcat(filename, "\\");
1034     }
1035     else /* specify the current directory */
1036         strcpy(filename, ".\\");
1037
1038     /* Create the full pathname */
1039     strcat(filename, file);
1040
1041     return (OpenFile(filename, &fileinfo,
1042                      OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1043 }
1044
1045 /******************************************************************************
1046  *   testFileExistenceW
1047  */
1048 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1049 {
1050     char *filename;
1051     DWORD pathlen, filelen;
1052     int ret;
1053     OFSTRUCT fileinfo;
1054
1055     fileinfo.cBytes = sizeof(OFSTRUCT);
1056
1057     pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1058     filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1059     filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1060
1061     WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1062     /* Add a trailing \ if necessary */
1063     if (pathlen > 1)
1064     {
1065         if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1066     }
1067     else /* specify the current directory */
1068         strcpy(filename, ".\\");
1069
1070     WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1071
1072     ret = (OpenFile(filename, &fileinfo,
1073                     OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1074     HeapFree( GetProcessHeap(), 0, filename );
1075     return ret;
1076 }
1077
1078 /*****************************************************************************
1079  *   VerFindFileA [VERSION.@]
1080  *
1081  *   Determines where to install a file based on whether it locates another
1082  *   version of the file in the system.  The values VerFindFile returns are
1083  *   used in a subsequent call to the VerInstallFile function.
1084  *
1085  *   Revision history:
1086  *      30-May-1997   Dave Cuthbert (dacut@ece.cmu.edu)
1087  *         Reimplementation of VerFindFile from original stub.
1088  */
1089 DWORD WINAPI VerFindFileA(
1090     DWORD flags,
1091     LPCSTR lpszFilename,
1092     LPCSTR lpszWinDir,
1093     LPCSTR lpszAppDir,
1094     LPSTR lpszCurDir,
1095     PUINT lpuCurDirLen,
1096     LPSTR lpszDestDir,
1097     PUINT lpuDestDirLen )
1098 {
1099     DWORD  retval = 0;
1100     const char *curDir;
1101     const char *destDir;
1102     unsigned int  curDirSizeReq;
1103     unsigned int  destDirSizeReq;
1104     char  systemDir[MAX_PATH];
1105
1106     /* Print out debugging information */
1107     TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1108           flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1109           lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1110           lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1111
1112     /* Figure out where the file should go; shared files default to the
1113        system directory */
1114
1115     GetSystemDirectoryA(systemDir, sizeof(systemDir));
1116     curDir = "";
1117     destDir = "";
1118
1119     if(flags & VFFF_ISSHAREDFILE)
1120     {
1121         destDir = systemDir;
1122         /* Were we given a filename?  If so, try to find the file. */
1123         if(lpszFilename)
1124         {
1125             if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1126             else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1127             {
1128                 curDir = lpszAppDir;
1129                 retval |= VFF_CURNEDEST;
1130             }
1131         }
1132     }
1133     else /* not a shared file */
1134     {
1135         if(lpszAppDir)
1136         {
1137             destDir = lpszAppDir;
1138             if(lpszFilename)
1139             {
1140                 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1141                 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1142                 {
1143                     curDir = systemDir;
1144                     retval |= VFF_CURNEDEST;
1145                 }
1146             }
1147         }
1148     }
1149
1150     /* Check to see if the file exists and is in use by another application */
1151     if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1152         if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1153            retval |= VFF_FILEINUSE;
1154     }
1155
1156     curDirSizeReq = strlen(curDir) + 1;
1157     destDirSizeReq = strlen(destDir) + 1;
1158
1159     /* Make sure that the pointers to the size of the buffers are
1160        valid; if not, do NOTHING with that buffer.  If that pointer
1161        is valid, then make sure that the buffer pointer is valid, too! */
1162
1163     if(lpuDestDirLen && lpszDestDir)
1164     {
1165         if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1166         lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1167         *lpuDestDirLen = destDirSizeReq;
1168     }
1169     if(lpuCurDirLen && lpszCurDir)
1170     {
1171         if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1172         lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1173         *lpuCurDirLen = curDirSizeReq;
1174     }
1175
1176     TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1177           (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1178           (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1179           (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1180           debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1181
1182     return retval;
1183 }
1184
1185 /*****************************************************************************
1186  * VerFindFileW                                         [VERSION.@]
1187  */
1188 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1189                            LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1190                            LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1191 {
1192     static const WCHAR emptyW;
1193     DWORD retval = 0;
1194     const WCHAR *curDir;
1195     const WCHAR *destDir;
1196     unsigned int curDirSizeReq;
1197     unsigned int destDirSizeReq;
1198     WCHAR systemDir[MAX_PATH];
1199
1200     /* Print out debugging information */
1201     TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1202           flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1203           lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1204           lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1205
1206     /* Figure out where the file should go; shared files default to the
1207        system directory */
1208
1209     GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1210     curDir = &emptyW;
1211     destDir = &emptyW;
1212
1213     if(flags & VFFF_ISSHAREDFILE)
1214     {
1215         destDir = systemDir;
1216         /* Were we given a filename?  If so, try to find the file. */
1217         if(lpszFilename)
1218         {
1219             if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1220             else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1221             {
1222                 curDir = lpszAppDir;
1223                 retval |= VFF_CURNEDEST;
1224             }
1225         }
1226     }
1227     else /* not a shared file */
1228     {
1229         if(lpszAppDir)
1230         {
1231             destDir = lpszAppDir;
1232             if(lpszFilename)
1233             {
1234                 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1235                 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1236                 {
1237                     curDir = systemDir;
1238                     retval |= VFF_CURNEDEST;
1239                 }
1240             }
1241         }
1242     }
1243
1244     if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1245         retval |= VFF_FILEINUSE;
1246
1247     curDirSizeReq = strlenW(curDir) + 1;
1248     destDirSizeReq = strlenW(destDir) + 1;
1249
1250     /* Make sure that the pointers to the size of the buffers are
1251        valid; if not, do NOTHING with that buffer.  If that pointer
1252        is valid, then make sure that the buffer pointer is valid, too! */
1253
1254     if(lpuDestDirLen && lpszDestDir)
1255     {
1256         if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1257         lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1258         *lpuDestDirLen = destDirSizeReq;
1259     }
1260     if(lpuCurDirLen && lpszCurDir)
1261     {
1262         if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1263         lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1264         *lpuCurDirLen = curDirSizeReq;
1265     }
1266
1267     TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1268           (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1269           (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1270           (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1271           debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1272     return retval;
1273 }
1274
1275 static LPBYTE
1276 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1277     DWORD       alloclen;
1278     LPBYTE      buf;
1279     DWORD       ret;
1280
1281     alloclen = 1000;
1282     buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1283     if(buf == NULL) {
1284         WARN("Memory exausted while fetching version info!\n");
1285         return NULL;
1286     }
1287     while (1) {
1288         ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1289         if (!ret) {
1290             HeapFree(GetProcessHeap(), 0, buf);
1291             return NULL;
1292         }
1293         if (alloclen<*(WORD*)buf) {
1294             alloclen = *(WORD*)buf;
1295             HeapFree(GetProcessHeap(), 0, buf);
1296             buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1297             if(buf == NULL) {
1298                WARN("Memory exausted while fetching version info!\n");
1299                return NULL;
1300             }
1301         } else {
1302             *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1303             if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1304                 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1305             if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1306                 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1307             return buf;
1308         }
1309     }
1310 }
1311
1312 static DWORD
1313 _error2vif(DWORD error) {
1314     switch (error) {
1315     case ERROR_ACCESS_DENIED:
1316         return VIF_ACCESSVIOLATION;
1317     case ERROR_SHARING_VIOLATION:
1318         return VIF_SHARINGVIOLATION;
1319     default:
1320         return 0;
1321     }
1322 }
1323
1324
1325 /******************************************************************************
1326  * VerInstallFileA [VERSION.@]
1327  */
1328 DWORD WINAPI VerInstallFileA(
1329         DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1330         LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1331 {
1332     LPCSTR pdest;
1333     char        destfn[260],tmpfn[260],srcfn[260];
1334     HFILE       hfsrc,hfdst;
1335     DWORD       attr,xret,tmplast;
1336     LONG        ret;
1337     LPBYTE      buf1,buf2;
1338     OFSTRUCT    ofs;
1339
1340     TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1341             flags,srcfilename,destfilename,srcdir,destdir,curdir,tmpfile,*tmpfilelen
1342     );
1343     xret = 0;
1344     sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1345     if (!destdir || !*destdir) pdest = srcdir;
1346     else pdest = destdir;
1347     sprintf(destfn,"%s\\%s",pdest,destfilename);
1348     hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1349     if (hfsrc < 0)
1350         return VIF_CANNOTREADSRC;
1351     sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1352     tmplast=strlen(pdest)+1;
1353     attr = GetFileAttributesA(tmpfn);
1354     if (attr != INVALID_FILE_ATTRIBUTES) {
1355         if (attr & FILE_ATTRIBUTE_READONLY) {
1356             LZClose(hfsrc);
1357             return VIF_WRITEPROT;
1358         }
1359         /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1360     }
1361     attr = INVALID_FILE_ATTRIBUTES;
1362     if (flags & VIFF_FORCEINSTALL) {
1363         if (tmpfile[0]) {
1364             sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1365             tmplast = strlen(pdest)+1;
1366             attr = GetFileAttributesA(tmpfn);
1367             /* if it exists, it has been copied by the call before.
1368              * we jump over the copy part...
1369              */
1370         }
1371     }
1372     if (attr == INVALID_FILE_ATTRIBUTES) {
1373         char    *s;
1374
1375         GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1376         s=strrchr(tmpfn,'\\');
1377         if (s)
1378             tmplast = s-tmpfn;
1379         else
1380             tmplast = 0;
1381         hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1382         if (hfdst == HFILE_ERROR) {
1383             LZClose(hfsrc);
1384             return VIF_CANNOTCREATE; /* | translated dos error */
1385         }
1386         ret = LZCopy(hfsrc,hfdst);
1387         _lclose(hfdst);
1388         if (ret < 0) {
1389             /* translate LZ errors into VIF_xxx */
1390             switch (ret) {
1391             case LZERROR_BADINHANDLE:
1392             case LZERROR_READ:
1393             case LZERROR_BADVALUE:
1394             case LZERROR_UNKNOWNALG:
1395                 xret = VIF_CANNOTREADSRC;
1396                 break;
1397             case LZERROR_BADOUTHANDLE:
1398             case LZERROR_WRITE:
1399                 xret = VIF_OUTOFSPACE;
1400                 break;
1401             case LZERROR_GLOBALLOC:
1402             case LZERROR_GLOBLOCK:
1403                 xret = VIF_OUTOFMEMORY;
1404                 break;
1405             default: /* unknown error, should not happen */
1406                 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1407                 xret = 0;
1408                 break;
1409             }
1410             if (xret) {
1411                 LZClose(hfsrc);
1412                 return xret;
1413             }
1414         }
1415     }
1416     if (!(flags & VIFF_FORCEINSTALL)) {
1417         VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1418         buf1 = _fetch_versioninfo(destfn,&destvffi);
1419         if (buf1) {
1420             buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1421             if (buf2) {
1422                 char    *tbuf1,*tbuf2;
1423                 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1424                 UINT    len1,len2;
1425
1426                 len1=len2=40;
1427
1428                 /* compare file versions */
1429                 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1430                     ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1431                      (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1432                     )
1433                 )
1434                     xret |= VIF_MISMATCH|VIF_SRCOLD;
1435                 /* compare filetypes and filesubtypes */
1436                 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1437                     (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1438                 )
1439                     xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1440                 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1441                     VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1442                 ) {
1443                     /* Do something with tbuf1 and tbuf2
1444                      * generates DIFFLANG|MISMATCH
1445                      */
1446                 }
1447                 HeapFree(GetProcessHeap(), 0, buf2);
1448             } else
1449                 xret=VIF_MISMATCH|VIF_SRCOLD;
1450             HeapFree(GetProcessHeap(), 0, buf1);
1451         }
1452     }
1453     if (xret) {
1454         if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1455             xret|=VIF_BUFFTOOSMALL;
1456             DeleteFileA(tmpfn);
1457         } else {
1458             strcpy(tmpfile,tmpfn+tmplast);
1459             *tmpfilelen = strlen(tmpfn+tmplast)+1;
1460             xret|=VIF_TEMPFILE;
1461         }
1462     } else {
1463         if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1464             if (!DeleteFileA(destfn)) {
1465                 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1466                 DeleteFileA(tmpfn);
1467                 LZClose(hfsrc);
1468                 return xret;
1469             }
1470         if ((!(flags & VIFF_DONTDELETEOLD))     &&
1471             curdir                              &&
1472             *curdir                             &&
1473             lstrcmpiA(curdir,pdest)
1474         ) {
1475             char curfn[260];
1476
1477             sprintf(curfn,"%s\\%s",curdir,destfilename);
1478             if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1479                 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1480                 if (!DeleteFileA(curfn))
1481                     xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1482             }
1483         }
1484         if (!MoveFileA(tmpfn,destfn)) {
1485             xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1486             DeleteFileA(tmpfn);
1487         }
1488     }
1489     LZClose(hfsrc);
1490     return xret;
1491 }
1492
1493
1494 /******************************************************************************
1495  * VerInstallFileW                              [VERSION.@]
1496  */
1497 DWORD WINAPI VerInstallFileW(
1498         DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1499         LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1500 {
1501     LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1502     DWORD ret = 0;
1503     UINT len;
1504
1505     if (srcfilename)
1506     {
1507         len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1508         if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1509             WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1510         else
1511             ret = VIF_OUTOFMEMORY;
1512     }
1513     if (srcdir && !ret)
1514     {
1515         len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1516         if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1517             WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1518         else
1519             ret = VIF_OUTOFMEMORY;
1520     }
1521     if (destfilename && !ret)
1522     {
1523         len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1524         if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1525             WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1526         else
1527             ret = VIF_OUTOFMEMORY;
1528     }
1529     if (destdir && !ret)
1530     {
1531         len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1532         if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1533             WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1534         else
1535             ret = VIF_OUTOFMEMORY;
1536     }
1537     if (curdir && !ret)
1538     {
1539         len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1540         if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1541             WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1542         else
1543             ret = VIF_OUTOFMEMORY;
1544     }
1545     if (!ret)
1546     {
1547         len = *tmpfilelen * sizeof(WCHAR);
1548         wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1549         if (!wtmpf)
1550             ret = VIF_OUTOFMEMORY;
1551     }
1552     if (!ret)
1553         ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1554     if (!ret)
1555         *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1556     else if (ret & VIF_BUFFTOOSMALL)
1557         *tmpfilelen = len;  /* FIXME: not correct */
1558
1559     HeapFree( GetProcessHeap(), 0, wsrcf );
1560     HeapFree( GetProcessHeap(), 0, wsrcd );
1561     HeapFree( GetProcessHeap(), 0, wdestf );
1562     HeapFree( GetProcessHeap(), 0, wdestd );
1563     HeapFree( GetProcessHeap(), 0, wtmpf );
1564     HeapFree( GetProcessHeap(), 0, wcurd );
1565     return ret;
1566 }