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