xref: /reactos/dll/win32/version/version.c (revision 0623a6f8)
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)
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 nullW[] = { 0 };
1058     static const WCHAR rootW[] = { '\\', 0 };
1059     static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
1060                                           '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
1061 
1062     const VS_VERSION_INFO_STRUCT32 *info = pBlock;
1063 
1064     TRACE("(%p,%s,%p,%p)\n",
1065                 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1066 
1067     if (!pBlock)
1068         return FALSE;
1069 
1070     if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
1071         lpSubBlock = rootW;
1072 
1073     if ( VersionInfoIs16( info ) )
1074     {
1075         BOOL ret;
1076         int len;
1077         LPSTR lpSubBlockA;
1078 
1079         len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1080         lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1081 
1082         if (!lpSubBlockA)
1083             return FALSE;
1084 
1085         WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1086 
1087         ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1088 
1089         HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1090 
1091         if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
1092         {
1093             /* Set lpBuffer so it points to the 'empty' area where we store
1094              * the converted strings
1095              */
1096             LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1097             DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1098             DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1099 
1100             len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1101                                       lpBufferW + pos, max/sizeof(WCHAR) - pos );
1102             *lplpBuffer = lpBufferW + pos;
1103             if (puLen) *puLen = len;
1104         }
1105         return ret;
1106     }
1107 
1108     return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
1109 }
1110 
1111 
1112 /******************************************************************************
1113  *   testFileExistenceA
1114  *
1115  *   Tests whether a given path/file combination exists.  If the file does
1116  *   not exist, the return value is zero.  If it does exist, the return
1117  *   value is non-zero.
1118  *
1119  *   Revision history
1120  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1121  *         Original implementation
1122  *
1123  */
1124 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1125 {
1126     char  filename[1024];
1127     int  filenamelen;
1128     OFSTRUCT  fileinfo;
1129 
1130     fileinfo.cBytes = sizeof(OFSTRUCT);
1131 
1132     if (path)
1133     {
1134         strcpy(filename, path);
1135         filenamelen = strlen(filename);
1136 
1137         /* Add a trailing \ if necessary */
1138         if(filenamelen)
1139         {
1140             if(filename[filenamelen - 1] != '\\')
1141                 strcat(filename, "\\");
1142         }
1143         else /* specify the current directory */
1144             strcpy(filename, ".\\");
1145     }
1146     else
1147         filename[0] = 0;
1148 
1149     /* Create the full pathname */
1150     strcat(filename, file);
1151 
1152     return (OpenFile(filename, &fileinfo,
1153                      OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1154 }
1155 
1156 /******************************************************************************
1157  *   testFileExistenceW
1158  */
1159 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1160 {
1161     char *filename;
1162     DWORD pathlen, filelen;
1163     int ret;
1164     OFSTRUCT fileinfo;
1165 
1166     fileinfo.cBytes = sizeof(OFSTRUCT);
1167 
1168     pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1169     filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1170     filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1171 
1172     WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1173     /* Add a trailing \ if necessary */
1174     if (pathlen > 1)
1175     {
1176         if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1177     }
1178     else /* specify the current directory */
1179         strcpy(filename, ".\\");
1180 
1181     WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1182 
1183     ret = (OpenFile(filename, &fileinfo,
1184                     OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1185     HeapFree( GetProcessHeap(), 0, filename );
1186     return ret;
1187 }
1188 
1189 /*****************************************************************************
1190  *   VerFindFileA [VERSION.@]
1191  *
1192  *   Determines where to install a file based on whether it locates another
1193  *   version of the file in the system.  The values VerFindFile returns are
1194  *   used in a subsequent call to the VerInstallFile function.
1195  *
1196  *   Revision history:
1197  *      30-May-1997   Dave Cuthbert (dacut@ece.cmu.edu)
1198  *         Reimplementation of VerFindFile from original stub.
1199  */
1200 DWORD WINAPI VerFindFileA(
1201     DWORD flags,
1202     LPCSTR lpszFilename,
1203     LPCSTR lpszWinDir,
1204     LPCSTR lpszAppDir,
1205     LPSTR lpszCurDir,
1206     PUINT lpuCurDirLen,
1207     LPSTR lpszDestDir,
1208     PUINT lpuDestDirLen )
1209 {
1210     DWORD  retval = 0;
1211     const char *curDir;
1212     const char *destDir;
1213     unsigned int  curDirSizeReq;
1214     unsigned int  destDirSizeReq;
1215     char winDir[MAX_PATH], systemDir[MAX_PATH];
1216 
1217     /* Print out debugging information */
1218     TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1219           flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1220           lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1221           lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1222 
1223     /* Figure out where the file should go; shared files default to the
1224        system directory */
1225 
1226     GetSystemDirectoryA(systemDir, sizeof(systemDir));
1227     curDir = "";
1228 
1229     if(flags & VFFF_ISSHAREDFILE)
1230     {
1231         destDir = systemDir;
1232         /* Were we given a filename?  If so, try to find the file. */
1233         if(lpszFilename)
1234         {
1235             if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1236             else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1237                 curDir = lpszAppDir;
1238 
1239             if(!testFileExistenceA(systemDir, lpszFilename, FALSE))
1240                 retval |= VFF_CURNEDEST;
1241         }
1242     }
1243     else /* not a shared file */
1244     {
1245         destDir = lpszAppDir ? lpszAppDir : "";
1246         if(lpszFilename)
1247         {
1248             GetWindowsDirectoryA( winDir, MAX_PATH );
1249             if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1250             else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1251                 curDir = winDir;
1252             else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1253                 curDir = systemDir;
1254 
1255             if (lpszAppDir && lpszAppDir[0])
1256             {
1257                 if(!testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1258                     retval |= VFF_CURNEDEST;
1259             }
1260             else if(testFileExistenceA(NULL, lpszFilename, FALSE))
1261                 retval |= VFF_CURNEDEST;
1262         }
1263     }
1264 
1265     /* Check to see if the file exists and is in use by another application */
1266     if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1267         if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1268            retval |= VFF_FILEINUSE;
1269     }
1270 
1271     curDirSizeReq = strlen(curDir) + 1;
1272     destDirSizeReq = strlen(destDir) + 1;
1273 
1274     /* Make sure that the pointers to the size of the buffers are
1275        valid; if not, do NOTHING with that buffer.  If that pointer
1276        is valid, then make sure that the buffer pointer is valid, too! */
1277 
1278     if(lpuDestDirLen && lpszDestDir)
1279     {
1280         if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1281         lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1282         *lpuDestDirLen = destDirSizeReq;
1283     }
1284     if(lpuCurDirLen && lpszCurDir)
1285     {
1286         if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1287         lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1288         *lpuCurDirLen = curDirSizeReq;
1289     }
1290 
1291     TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1292           (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1293           (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1294           (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1295           debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1296 
1297     return retval;
1298 }
1299 
1300 /*****************************************************************************
1301  * VerFindFileW						[VERSION.@]
1302  */
1303 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1304                            LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1305                            LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1306 {
1307     static const WCHAR emptyW;
1308     DWORD retval = 0;
1309     const WCHAR *curDir;
1310     const WCHAR *destDir;
1311     unsigned int curDirSizeReq;
1312     unsigned int destDirSizeReq;
1313     WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1314 
1315     /* Print out debugging information */
1316     TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1317           flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1318           lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1319           lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1320 
1321     /* Figure out where the file should go; shared files default to the
1322        system directory */
1323 
1324     GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1325     curDir = &emptyW;
1326 
1327     if(flags & VFFF_ISSHAREDFILE)
1328     {
1329         destDir = systemDir;
1330         /* Were we given a filename?  If so, try to find the file. */
1331         if(lpszFilename)
1332         {
1333             if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1334             else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1335             {
1336                 curDir = lpszAppDir;
1337                 retval |= VFF_CURNEDEST;
1338             }
1339         }
1340     }
1341     else /* not a shared file */
1342     {
1343         destDir = lpszAppDir ? lpszAppDir : &emptyW;
1344         if(lpszFilename)
1345         {
1346             GetWindowsDirectoryW( winDir, MAX_PATH );
1347             if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1348             else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1349             {
1350                 curDir = winDir;
1351                 retval |= VFF_CURNEDEST;
1352             }
1353             else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1354             {
1355                 curDir = systemDir;
1356                 retval |= VFF_CURNEDEST;
1357             }
1358         }
1359     }
1360 
1361     if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1362         retval |= VFF_FILEINUSE;
1363 
1364     curDirSizeReq = strlenW(curDir) + 1;
1365     destDirSizeReq = strlenW(destDir) + 1;
1366 
1367     /* Make sure that the pointers to the size of the buffers are
1368        valid; if not, do NOTHING with that buffer.  If that pointer
1369        is valid, then make sure that the buffer pointer is valid, too! */
1370 
1371     if(lpuDestDirLen && lpszDestDir)
1372     {
1373         if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1374         lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1375         *lpuDestDirLen = destDirSizeReq;
1376     }
1377     if(lpuCurDirLen && lpszCurDir)
1378     {
1379         if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1380         lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1381         *lpuCurDirLen = curDirSizeReq;
1382     }
1383 
1384     TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1385           (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1386           (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1387           (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1388           debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1389     return retval;
1390 }
1391 
1392 static LPBYTE
1393 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1394     DWORD	alloclen;
1395     LPBYTE	buf;
1396     DWORD	ret;
1397 
1398     alloclen = 1000;
1399     buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1400     if(buf == NULL) {
1401         WARN("Memory exhausted while fetching version info!\n");
1402         return NULL;
1403     }
1404     while (1) {
1405 	ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1406 	if (!ret) {
1407 	    HeapFree(GetProcessHeap(), 0, buf);
1408 	    return NULL;
1409 	}
1410 	if (alloclen<*(WORD*)buf) {
1411 	    alloclen = *(WORD*)buf;
1412 	    HeapFree(GetProcessHeap(), 0, buf);
1413 	    buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1414             if(buf == NULL) {
1415                WARN("Memory exhausted while fetching version info!\n");
1416                return NULL;
1417             }
1418 	} else {
1419 	    *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1420 	    if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1421 		*vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1422 	    if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1423                 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1424 	    return buf;
1425 	}
1426     }
1427 }
1428 
1429 static DWORD
1430 _error2vif(DWORD error) {
1431     switch (error) {
1432     case ERROR_ACCESS_DENIED:
1433 	return VIF_ACCESSVIOLATION;
1434     case ERROR_SHARING_VIOLATION:
1435 	return VIF_SHARINGVIOLATION;
1436     default:
1437 	return 0;
1438     }
1439 }
1440 
1441 
1442 /******************************************************************************
1443  * VerInstallFileA [VERSION.@]
1444  */
1445 DWORD WINAPI VerInstallFileA(
1446 	DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1447 	LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1448 {
1449     LPCSTR pdest;
1450     char	destfn[260],tmpfn[260],srcfn[260];
1451     HFILE	hfsrc,hfdst;
1452     DWORD	attr,xret,tmplast;
1453     LONG	ret;
1454     LPBYTE	buf1,buf2;
1455     OFSTRUCT	ofs;
1456 
1457     TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1458           flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1459           debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1460           tmpfile,*tmpfilelen);
1461     xret = 0;
1462     if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1463     sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1464     if (!destdir || !*destdir) pdest = srcdir;
1465     else pdest = destdir;
1466     sprintf(destfn,"%s\\%s",pdest,destfilename);
1467     hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1468     if (hfsrc < 0)
1469 	return VIF_CANNOTREADSRC;
1470     sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1471     tmplast=strlen(pdest)+1;
1472     attr = GetFileAttributesA(tmpfn);
1473     if (attr != INVALID_FILE_ATTRIBUTES) {
1474 	if (attr & FILE_ATTRIBUTE_READONLY) {
1475 	    LZClose(hfsrc);
1476 	    return VIF_WRITEPROT;
1477 	}
1478 	/* FIXME: check if file currently in use and return VIF_FILEINUSE */
1479     }
1480     attr = INVALID_FILE_ATTRIBUTES;
1481     if (flags & VIFF_FORCEINSTALL) {
1482 	if (tmpfile[0]) {
1483 	    sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1484 	    tmplast = strlen(pdest)+1;
1485 	    attr = GetFileAttributesA(tmpfn);
1486 	    /* if it exists, it has been copied by the call before.
1487 	     * we jump over the copy part...
1488 	     */
1489 	}
1490     }
1491     if (attr == INVALID_FILE_ATTRIBUTES) {
1492 	char	*s;
1493 
1494 	GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1495 	s=strrchr(tmpfn,'\\');
1496 	if (s)
1497 	    tmplast = s-tmpfn;
1498 	else
1499 	    tmplast = 0;
1500 	hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1501 	if (hfdst == HFILE_ERROR) {
1502 	    LZClose(hfsrc);
1503 	    return VIF_CANNOTCREATE; /* | translated dos error */
1504 	}
1505 	ret = LZCopy(hfsrc,hfdst);
1506 	_lclose(hfdst);
1507 	if (ret < 0) {
1508 	    /* translate LZ errors into VIF_xxx */
1509 	    switch (ret) {
1510 	    case LZERROR_BADINHANDLE:
1511 	    case LZERROR_READ:
1512 	    case LZERROR_BADVALUE:
1513 	    case LZERROR_UNKNOWNALG:
1514 		xret = VIF_CANNOTREADSRC;
1515 		break;
1516 	    case LZERROR_BADOUTHANDLE:
1517 	    case LZERROR_WRITE:
1518 		xret = VIF_OUTOFSPACE;
1519 		break;
1520 	    case LZERROR_GLOBALLOC:
1521 	    case LZERROR_GLOBLOCK:
1522 		xret = VIF_OUTOFMEMORY;
1523 		break;
1524 	    default: /* unknown error, should not happen */
1525 		FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1526 		xret = 0;
1527 		break;
1528 	    }
1529 	    if (xret) {
1530 		LZClose(hfsrc);
1531 		return xret;
1532 	    }
1533 	}
1534     }
1535     if (!(flags & VIFF_FORCEINSTALL)) {
1536 	VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1537 	buf1 = _fetch_versioninfo(destfn,&destvffi);
1538 	if (buf1) {
1539 	    buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1540 	    if (buf2) {
1541 		char	*tbuf1,*tbuf2;
1542 		static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1543 		UINT	len1,len2;
1544 
1545 		len1=len2=40;
1546 
1547 		/* compare file versions */
1548 		if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1549 		    ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1550 		     (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1551 		    )
1552 		)
1553 		    xret |= VIF_MISMATCH|VIF_SRCOLD;
1554 		/* compare filetypes and filesubtypes */
1555 		if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1556 		    (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1557 		)
1558 		    xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1559 		if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1560 		    VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1561 		) {
1562                     /* Do something with tbuf1 and tbuf2
1563 		     * generates DIFFLANG|MISMATCH
1564 		     */
1565 		}
1566 		HeapFree(GetProcessHeap(), 0, buf2);
1567 	    } else
1568 		xret=VIF_MISMATCH|VIF_SRCOLD;
1569 	    HeapFree(GetProcessHeap(), 0, buf1);
1570 	}
1571     }
1572     if (xret) {
1573 	if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1574 	    xret|=VIF_BUFFTOOSMALL;
1575 	    DeleteFileA(tmpfn);
1576 	} else {
1577 	    strcpy(tmpfile,tmpfn+tmplast);
1578 	    *tmpfilelen = strlen(tmpfn+tmplast)+1;
1579 	    xret|=VIF_TEMPFILE;
1580 	}
1581     } else {
1582 	if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1583 	    if (!DeleteFileA(destfn)) {
1584 		xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1585 		DeleteFileA(tmpfn);
1586 		LZClose(hfsrc);
1587 		return xret;
1588 	    }
1589 	if ((!(flags & VIFF_DONTDELETEOLD))	&&
1590 	    curdir				&&
1591 	    *curdir				&&
1592 	    lstrcmpiA(curdir,pdest)
1593 	) {
1594 	    char curfn[260];
1595 
1596 	    sprintf(curfn,"%s\\%s",curdir,destfilename);
1597 	    if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1598 		/* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1599 		if (!DeleteFileA(curfn))
1600 		    xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1601 	    }
1602 	}
1603 	if (!MoveFileA(tmpfn,destfn)) {
1604 	    xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1605 	    DeleteFileA(tmpfn);
1606 	}
1607     }
1608     LZClose(hfsrc);
1609     return xret;
1610 }
1611 
1612 
1613 /******************************************************************************
1614  * VerInstallFileW				[VERSION.@]
1615  */
1616 DWORD WINAPI VerInstallFileW(
1617 	DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1618 	LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1619 {
1620     LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1621     DWORD ret = 0;
1622     UINT len;
1623 
1624     if (srcfilename)
1625     {
1626         len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1627         if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1628             WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1629         else
1630             ret = VIF_OUTOFMEMORY;
1631     }
1632     if (srcdir && !ret)
1633     {
1634         len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1635         if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1636             WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1637         else
1638             ret = VIF_OUTOFMEMORY;
1639     }
1640     if (destfilename && !ret)
1641     {
1642         len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1643         if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1644             WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1645         else
1646             ret = VIF_OUTOFMEMORY;
1647     }
1648     if (destdir && !ret)
1649     {
1650         len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1651         if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1652             WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1653         else
1654             ret = VIF_OUTOFMEMORY;
1655     }
1656     if (curdir && !ret)
1657     {
1658         len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1659         if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1660             WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1661         else
1662             ret = VIF_OUTOFMEMORY;
1663     }
1664     if (!ret)
1665     {
1666         len = *tmpfilelen * sizeof(WCHAR);
1667         wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1668         if (!wtmpf)
1669             ret = VIF_OUTOFMEMORY;
1670     }
1671     if (!ret)
1672         ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1673     if (!ret)
1674         *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1675     else if (ret & VIF_BUFFTOOSMALL)
1676         *tmpfilelen = len;  /* FIXME: not correct */
1677 
1678     HeapFree( GetProcessHeap(), 0, wsrcf );
1679     HeapFree( GetProcessHeap(), 0, wsrcd );
1680     HeapFree( GetProcessHeap(), 0, wdestf );
1681     HeapFree( GetProcessHeap(), 0, wdestd );
1682     HeapFree( GetProcessHeap(), 0, wtmpf );
1683     HeapFree( GetProcessHeap(), 0, wcurd );
1684     return ret;
1685 }
1686