[VERSION] Sync with Wine 3.0. CORE-14225
[reactos.git] / dll / win32 / version / version.c
1 /*
2 * Implementation of VERSION.DLL
3 *
4 * Copyright 1996,1997 Marcus Meissner
5 * Copyright 1997 David Cuthbert
6 * Copyright 1999 Ulrich Weigand
7 * Copyright 2005 Paul Vriens
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 */
24 #include "config.h"
25
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 #include <sys/types.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35
36 #define NONAMELESSUNION
37 #define NONAMELESSSTRUCT
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winver.h"
41 #include "winuser.h"
42 #include "winnls.h"
43 #include "winternl.h"
44 #include "lzexpand.h"
45 #include "wine/unicode.h"
46 #include "winerror.h"
47 #include "wine/debug.h"
48
49
50 WINE_DEFAULT_DEBUG_CHANNEL(ver);
51
52 typedef struct
53 {
54 WORD offset;
55 WORD length;
56 WORD flags;
57 WORD id;
58 WORD handle;
59 WORD usage;
60 } NE_NAMEINFO;
61
62 typedef struct
63 {
64 WORD type_id;
65 WORD count;
66 DWORD resloader;
67 } NE_TYPEINFO;
68
69 /**********************************************************************
70 * find_entry_by_id
71 *
72 * Find an entry by id in a resource directory
73 * Copied from loader/pe_resource.c
74 */
75 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
76 WORD id, const void *root )
77 {
78 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
79 int min, max, pos;
80
81 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
82 min = dir->NumberOfNamedEntries;
83 max = min + dir->NumberOfIdEntries - 1;
84 while (min <= max)
85 {
86 pos = (min + max) / 2;
87 if (entry[pos].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 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
709 return 0;
710 }
711 }
712
713 /******************************************************************************
714 * GetFileVersionInfoSizeExA [VERSION.@]
715 */
716 DWORD WINAPI GetFileVersionInfoSizeExA( DWORD flags, LPCSTR filename, LPDWORD handle )
717 {
718 UNICODE_STRING filenameW;
719 DWORD retval;
720
721 TRACE("(0x%x,%s,%p)\n", flags, debugstr_a(filename), handle );
722
723 if(filename)
724 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
725 else
726 filenameW.Buffer = NULL;
727
728 retval = GetFileVersionInfoSizeExW(flags, filenameW.Buffer, handle);
729
730 RtlFreeUnicodeString(&filenameW);
731
732 return retval;
733 }
734
735 /***********************************************************************
736 * GetFileVersionInfoExW [VERSION.@]
737 */
738 BOOL WINAPI GetFileVersionInfoExW( DWORD flags, LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
739 {
740 static const char signature[4] = "FE2X";
741 DWORD len, offset, magic = 1;
742 HFILE lzfd;
743 OFSTRUCT ofs;
744 HMODULE hModule;
745 VS_VERSION_INFO_STRUCT32* vvis = data;
746
747 TRACE("(0x%x,%s,%d,size=%d,data=%p)\n",
748 flags, debugstr_w(filename), handle, datasize, data );
749
750 if (!data)
751 {
752 SetLastError(ERROR_INVALID_DATA);
753 return FALSE;
754 }
755 if (flags & ~FILE_VER_GET_LOCALISED)
756 FIXME("flags 0x%x ignored\n", flags & ~FILE_VER_GET_LOCALISED);
757
758 if ((lzfd = LZOpenFileW( (LPWSTR)filename, &ofs, OF_READ )) != HFILE_ERROR)
759 {
760 if ((magic = find_version_resource( lzfd, &len, &offset, flags )) > 1)
761 {
762 LZSeek( lzfd, offset, 0 /* SEEK_SET */ );
763 len = LZRead( lzfd, data, min( len, datasize ) );
764 }
765 LZClose( lzfd );
766 }
767
768 if ((magic == 1) && (hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_DATAFILE )))
769 {
770 HRSRC hRsrc = NULL;
771 if (!(flags & FILE_VER_GET_LOCALISED))
772 {
773 LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT );
774 hRsrc = FindResourceExW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
775 (LPWSTR)VS_FILE_INFO, english );
776 }
777 if (!hRsrc)
778 hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO),
779 (LPWSTR)VS_FILE_INFO );
780 if (hRsrc)
781 {
782 HGLOBAL hMem = LoadResource( hModule, hRsrc );
783 magic = IMAGE_NT_SIGNATURE;
784 len = min( SizeofResource(hModule, hRsrc), datasize );
785 memcpy( data, LockResource( hMem ), len );
786 FreeResource( hMem );
787 }
788 FreeLibrary( hModule );
789 }
790
791 switch (magic)
792 {
793 case IMAGE_OS2_SIGNATURE:
794 /* We have a 16bit resource. */
795 if (TRACE_ON(ver))
796 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data ));
797 SetLastError(0);
798 return TRUE;
799
800 case IMAGE_NT_SIGNATURE:
801 /* We have a 32bit resource.
802 *
803 * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
804 * This extra buffer is used for Unicode to ANSI conversions in A-Calls
805 */
806 len = vvis->wLength + sizeof(signature);
807 if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) );
808 if (TRACE_ON(ver))
809 print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis ));
810 SetLastError(0);
811 return TRUE;
812
813 default:
814 SetLastError( lzfd == HFILE_ERROR ? ofs.nErrCode : ERROR_RESOURCE_DATA_NOT_FOUND );
815 return FALSE;
816 }
817 }
818
819 /***********************************************************************
820 * GetFileVersionInfoExA [VERSION.@]
821 */
822 BOOL WINAPI GetFileVersionInfoExA( DWORD flags, LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
823 {
824 UNICODE_STRING filenameW;
825 BOOL retval;
826
827 TRACE("(0x%x,%s,%d,size=%d,data=%p)\n",
828 flags, debugstr_a(filename), handle, datasize, data );
829
830 if(filename)
831 RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
832 else
833 filenameW.Buffer = NULL;
834
835 retval = GetFileVersionInfoExW(flags, filenameW.Buffer, handle, datasize, data);
836
837 RtlFreeUnicodeString(&filenameW);
838
839 return retval;
840 }
841
842 /***********************************************************************
843 * GetFileVersionInfoW [VERSION.@]
844 */
845 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data )
846 {
847 return GetFileVersionInfoExW(FILE_VER_GET_LOCALISED, filename, handle, datasize, data);
848 }
849
850 /***********************************************************************
851 * GetFileVersionInfoA [VERSION.@]
852 */
853 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data )
854 {
855 return GetFileVersionInfoExA(FILE_VER_GET_LOCALISED, filename, handle, datasize, data);
856 }
857
858 /***********************************************************************
859 * VersionInfo16_FindChild [internal]
860 */
861 static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info,
862 LPCSTR szKey, UINT cbKey )
863 {
864 const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
865
866 while ((char *)child < (char *)info + info->wLength )
867 {
868 if (!strncasecmp( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
869 return child;
870
871 if (!(child->wLength)) return NULL;
872 child = VersionInfo16_Next( child );
873 }
874
875 return NULL;
876 }
877
878 /***********************************************************************
879 * VersionInfo32_FindChild [internal]
880 */
881 static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info,
882 LPCWSTR szKey, UINT cbKey )
883 {
884 const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
885
886 while ((char *)child < (char *)info + info->wLength )
887 {
888 if (!strncmpiW( child->szKey, szKey, cbKey ) && !child->szKey[cbKey])
889 return child;
890
891 if (!(child->wLength)) return NULL;
892 child = VersionInfo32_Next( child );
893 }
894
895 return NULL;
896 }
897
898 /***********************************************************************
899 * VersionInfo16_QueryValue [internal]
900 *
901 * Gets a value from a 16-bit NE resource
902 */
903 static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
904 LPVOID *lplpBuffer, UINT *puLen )
905 {
906 while ( *lpSubBlock )
907 {
908 /* Find next path component */
909 LPCSTR lpNextSlash;
910 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
911 if ( *lpNextSlash == '\\' )
912 break;
913
914 /* Skip empty components */
915 if ( lpNextSlash == lpSubBlock )
916 {
917 lpSubBlock++;
918 continue;
919 }
920
921 /* We have a non-empty component: search info for key */
922 info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
923 if ( !info )
924 {
925 if (puLen) *puLen = 0 ;
926 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
927 return FALSE;
928 }
929
930 /* Skip path component */
931 lpSubBlock = lpNextSlash;
932 }
933
934 /* Return value */
935 *lplpBuffer = VersionInfo16_Value( info );
936 if (puLen)
937 *puLen = info->wValueLength;
938
939 return TRUE;
940 }
941
942 /***********************************************************************
943 * VersionInfo32_QueryValue [internal]
944 *
945 * Gets a value from a 32-bit PE resource
946 */
947 static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
948 LPVOID *lplpBuffer, UINT *puLen, BOOL *pbText )
949 {
950 TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
951
952 while ( *lpSubBlock )
953 {
954 /* Find next path component */
955 LPCWSTR lpNextSlash;
956 for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
957 if ( *lpNextSlash == '\\' )
958 break;
959
960 /* Skip empty components */
961 if ( lpNextSlash == lpSubBlock )
962 {
963 lpSubBlock++;
964 continue;
965 }
966
967 /* We have a non-empty component: search info for key */
968 info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
969 if ( !info )
970 {
971 if (puLen) *puLen = 0 ;
972 SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND );
973 return FALSE;
974 }
975
976 /* Skip path component */
977 lpSubBlock = lpNextSlash;
978 }
979
980 /* Return value */
981 *lplpBuffer = VersionInfo32_Value( info );
982 if (puLen)
983 *puLen = info->wValueLength;
984 if (pbText)
985 *pbText = info->wType;
986
987 return TRUE;
988 }
989
990 /***********************************************************************
991 * VerQueryValueA [VERSION.@]
992 */
993 BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock,
994 LPVOID *lplpBuffer, PUINT puLen )
995 {
996 static const char rootA[] = "\\";
997 const VS_VERSION_INFO_STRUCT16 *info = pBlock;
998
999 TRACE("(%p,%s,%p,%p)\n",
1000 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
1001
1002 if (!pBlock)
1003 return FALSE;
1004
1005 if (lpSubBlock == NULL || lpSubBlock[0] == '\0')
1006 lpSubBlock = rootA;
1007
1008 if ( !VersionInfoIs16( info ) )
1009 {
1010 BOOL ret, isText;
1011 INT len;
1012 LPWSTR lpSubBlockW;
1013 UINT value_len;
1014
1015 len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
1016 lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1017
1018 if (!lpSubBlockW)
1019 return FALSE;
1020
1021 MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
1022
1023 ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, &value_len, &isText);
1024 if (puLen) *puLen = value_len;
1025
1026 HeapFree(GetProcessHeap(), 0, lpSubBlockW);
1027
1028 if (ret && isText)
1029 {
1030 /* Set lpBuffer so it points to the 'empty' area where we store
1031 * the converted strings
1032 */
1033 LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
1034 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1035 len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, value_len,
1036 lpBufferA + pos, info->wLength - pos, NULL, NULL);
1037 *lplpBuffer = lpBufferA + pos;
1038 if (puLen) *puLen = len;
1039 }
1040 return ret;
1041 }
1042
1043 return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
1044 }
1045
1046 /***********************************************************************
1047 * VerQueryValueW [VERSION.@]
1048 */
1049 BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock,
1050 LPVOID *lplpBuffer, PUINT puLen )
1051 {
1052 static const WCHAR nullW[] = { 0 };
1053 static const WCHAR rootW[] = { '\\', 0 };
1054 static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
1055 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
1056
1057 const VS_VERSION_INFO_STRUCT32 *info = pBlock;
1058
1059 TRACE("(%p,%s,%p,%p)\n",
1060 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
1061
1062 if (!pBlock)
1063 return FALSE;
1064
1065 if (lpSubBlock == NULL || lpSubBlock[0] == nullW[0])
1066 lpSubBlock = rootW;
1067
1068 if ( VersionInfoIs16( info ) )
1069 {
1070 BOOL ret;
1071 int len;
1072 LPSTR lpSubBlockA;
1073
1074 len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
1075 lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
1076
1077 if (!lpSubBlockA)
1078 return FALSE;
1079
1080 WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
1081
1082 ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
1083
1084 HeapFree(GetProcessHeap(), 0, lpSubBlockA);
1085
1086 if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
1087 {
1088 /* Set lpBuffer so it points to the 'empty' area where we store
1089 * the converted strings
1090 */
1091 LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
1092 DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock;
1093 DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
1094
1095 len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1,
1096 lpBufferW + pos, max/sizeof(WCHAR) - pos );
1097 *lplpBuffer = lpBufferW + pos;
1098 if (puLen) *puLen = len;
1099 }
1100 return ret;
1101 }
1102
1103 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
1104 }
1105
1106
1107 /******************************************************************************
1108 * testFileExistenceA
1109 *
1110 * Tests whether a given path/file combination exists. If the file does
1111 * not exist, the return value is zero. If it does exist, the return
1112 * value is non-zero.
1113 *
1114 * Revision history
1115 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1116 * Original implementation
1117 *
1118 */
1119 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
1120 {
1121 char filename[1024];
1122 int filenamelen;
1123 OFSTRUCT fileinfo;
1124
1125 fileinfo.cBytes = sizeof(OFSTRUCT);
1126
1127 strcpy(filename, path);
1128 filenamelen = strlen(filename);
1129
1130 /* Add a trailing \ if necessary */
1131 if(filenamelen) {
1132 if(filename[filenamelen - 1] != '\\')
1133 strcat(filename, "\\");
1134 }
1135 else /* specify the current directory */
1136 strcpy(filename, ".\\");
1137
1138 /* Create the full pathname */
1139 strcat(filename, file);
1140
1141 return (OpenFile(filename, &fileinfo,
1142 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1143 }
1144
1145 /******************************************************************************
1146 * testFileExistenceW
1147 */
1148 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
1149 {
1150 char *filename;
1151 DWORD pathlen, filelen;
1152 int ret;
1153 OFSTRUCT fileinfo;
1154
1155 fileinfo.cBytes = sizeof(OFSTRUCT);
1156
1157 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
1158 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
1159 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
1160
1161 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
1162 /* Add a trailing \ if necessary */
1163 if (pathlen > 1)
1164 {
1165 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
1166 }
1167 else /* specify the current directory */
1168 strcpy(filename, ".\\");
1169
1170 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
1171
1172 ret = (OpenFile(filename, &fileinfo,
1173 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
1174 HeapFree( GetProcessHeap(), 0, filename );
1175 return ret;
1176 }
1177
1178 /*****************************************************************************
1179 * VerFindFileA [VERSION.@]
1180 *
1181 * Determines where to install a file based on whether it locates another
1182 * version of the file in the system. The values VerFindFile returns are
1183 * used in a subsequent call to the VerInstallFile function.
1184 *
1185 * Revision history:
1186 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
1187 * Reimplementation of VerFindFile from original stub.
1188 */
1189 DWORD WINAPI VerFindFileA(
1190 DWORD flags,
1191 LPCSTR lpszFilename,
1192 LPCSTR lpszWinDir,
1193 LPCSTR lpszAppDir,
1194 LPSTR lpszCurDir,
1195 PUINT lpuCurDirLen,
1196 LPSTR lpszDestDir,
1197 PUINT lpuDestDirLen )
1198 {
1199 DWORD retval = 0;
1200 const char *curDir;
1201 const char *destDir;
1202 unsigned int curDirSizeReq;
1203 unsigned int destDirSizeReq;
1204 char winDir[MAX_PATH], systemDir[MAX_PATH];
1205
1206 /* Print out debugging information */
1207 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1208 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
1209 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1210 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1211
1212 /* Figure out where the file should go; shared files default to the
1213 system directory */
1214
1215 GetSystemDirectoryA(systemDir, sizeof(systemDir));
1216 curDir = "";
1217
1218 if(flags & VFFF_ISSHAREDFILE)
1219 {
1220 destDir = systemDir;
1221 /* Were we given a filename? If so, try to find the file. */
1222 if(lpszFilename)
1223 {
1224 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1225 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
1226 {
1227 curDir = lpszAppDir;
1228 retval |= VFF_CURNEDEST;
1229 }
1230 }
1231 }
1232 else /* not a shared file */
1233 {
1234 destDir = lpszAppDir ? lpszAppDir : "";
1235 if(lpszFilename)
1236 {
1237 GetWindowsDirectoryA( winDir, MAX_PATH );
1238 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
1239 else if(testFileExistenceA(winDir, lpszFilename, FALSE))
1240 {
1241 curDir = winDir;
1242 retval |= VFF_CURNEDEST;
1243 }
1244 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
1245 {
1246 curDir = systemDir;
1247 retval |= VFF_CURNEDEST;
1248 }
1249 }
1250 }
1251
1252 /* Check to see if the file exists and is in use by another application */
1253 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
1254 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
1255 retval |= VFF_FILEINUSE;
1256 }
1257
1258 curDirSizeReq = strlen(curDir) + 1;
1259 destDirSizeReq = strlen(destDir) + 1;
1260
1261 /* Make sure that the pointers to the size of the buffers are
1262 valid; if not, do NOTHING with that buffer. If that pointer
1263 is valid, then make sure that the buffer pointer is valid, too! */
1264
1265 if(lpuDestDirLen && lpszDestDir)
1266 {
1267 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1268 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
1269 *lpuDestDirLen = destDirSizeReq;
1270 }
1271 if(lpuCurDirLen && lpszCurDir)
1272 {
1273 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1274 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
1275 *lpuCurDirLen = curDirSizeReq;
1276 }
1277
1278 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1279 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1280 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1281 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1282 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
1283
1284 return retval;
1285 }
1286
1287 /*****************************************************************************
1288 * VerFindFileW [VERSION.@]
1289 */
1290 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
1291 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
1292 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
1293 {
1294 static const WCHAR emptyW;
1295 DWORD retval = 0;
1296 const WCHAR *curDir;
1297 const WCHAR *destDir;
1298 unsigned int curDirSizeReq;
1299 unsigned int destDirSizeReq;
1300 WCHAR winDir[MAX_PATH], systemDir[MAX_PATH];
1301
1302 /* Print out debugging information */
1303 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
1304 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
1305 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
1306 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
1307
1308 /* Figure out where the file should go; shared files default to the
1309 system directory */
1310
1311 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
1312 curDir = &emptyW;
1313
1314 if(flags & VFFF_ISSHAREDFILE)
1315 {
1316 destDir = systemDir;
1317 /* Were we given a filename? If so, try to find the file. */
1318 if(lpszFilename)
1319 {
1320 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1321 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
1322 {
1323 curDir = lpszAppDir;
1324 retval |= VFF_CURNEDEST;
1325 }
1326 }
1327 }
1328 else /* not a shared file */
1329 {
1330 destDir = lpszAppDir ? lpszAppDir : &emptyW;
1331 if(lpszFilename)
1332 {
1333 GetWindowsDirectoryW( winDir, MAX_PATH );
1334 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
1335 else if(testFileExistenceW(winDir, lpszFilename, FALSE))
1336 {
1337 curDir = winDir;
1338 retval |= VFF_CURNEDEST;
1339 }
1340 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
1341 {
1342 curDir = systemDir;
1343 retval |= VFF_CURNEDEST;
1344 }
1345 }
1346 }
1347
1348 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
1349 retval |= VFF_FILEINUSE;
1350
1351 curDirSizeReq = strlenW(curDir) + 1;
1352 destDirSizeReq = strlenW(destDir) + 1;
1353
1354 /* Make sure that the pointers to the size of the buffers are
1355 valid; if not, do NOTHING with that buffer. If that pointer
1356 is valid, then make sure that the buffer pointer is valid, too! */
1357
1358 if(lpuDestDirLen && lpszDestDir)
1359 {
1360 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1361 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
1362 *lpuDestDirLen = destDirSizeReq;
1363 }
1364 if(lpuCurDirLen && lpszCurDir)
1365 {
1366 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
1367 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
1368 *lpuCurDirLen = curDirSizeReq;
1369 }
1370
1371 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
1372 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
1373 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
1374 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
1375 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
1376 return retval;
1377 }
1378
1379 static LPBYTE
1380 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
1381 DWORD alloclen;
1382 LPBYTE buf;
1383 DWORD ret;
1384
1385 alloclen = 1000;
1386 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
1387 if(buf == NULL) {
1388 WARN("Memory exhausted while fetching version info!\n");
1389 return NULL;
1390 }
1391 while (1) {
1392 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
1393 if (!ret) {
1394 HeapFree(GetProcessHeap(), 0, buf);
1395 return NULL;
1396 }
1397 if (alloclen<*(WORD*)buf) {
1398 alloclen = *(WORD*)buf;
1399 HeapFree(GetProcessHeap(), 0, buf);
1400 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
1401 if(buf == NULL) {
1402 WARN("Memory exhausted while fetching version info!\n");
1403 return NULL;
1404 }
1405 } else {
1406 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
1407 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
1408 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
1409 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
1410 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
1411 return buf;
1412 }
1413 }
1414 }
1415
1416 static DWORD
1417 _error2vif(DWORD error) {
1418 switch (error) {
1419 case ERROR_ACCESS_DENIED:
1420 return VIF_ACCESSVIOLATION;
1421 case ERROR_SHARING_VIOLATION:
1422 return VIF_SHARINGVIOLATION;
1423 default:
1424 return 0;
1425 }
1426 }
1427
1428
1429 /******************************************************************************
1430 * VerInstallFileA [VERSION.@]
1431 */
1432 DWORD WINAPI VerInstallFileA(
1433 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
1434 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
1435 {
1436 LPCSTR pdest;
1437 char destfn[260],tmpfn[260],srcfn[260];
1438 HFILE hfsrc,hfdst;
1439 DWORD attr,xret,tmplast;
1440 LONG ret;
1441 LPBYTE buf1,buf2;
1442 OFSTRUCT ofs;
1443
1444 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
1445 flags,debugstr_a(srcfilename),debugstr_a(destfilename),
1446 debugstr_a(srcdir),debugstr_a(destdir),debugstr_a(curdir),
1447 tmpfile,*tmpfilelen);
1448 xret = 0;
1449 if (!srcdir || !srcfilename) return VIF_CANNOTREADSRC;
1450 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
1451 if (!destdir || !*destdir) pdest = srcdir;
1452 else pdest = destdir;
1453 sprintf(destfn,"%s\\%s",pdest,destfilename);
1454 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
1455 if (hfsrc < 0)
1456 return VIF_CANNOTREADSRC;
1457 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
1458 tmplast=strlen(pdest)+1;
1459 attr = GetFileAttributesA(tmpfn);
1460 if (attr != INVALID_FILE_ATTRIBUTES) {
1461 if (attr & FILE_ATTRIBUTE_READONLY) {
1462 LZClose(hfsrc);
1463 return VIF_WRITEPROT;
1464 }
1465 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
1466 }
1467 attr = INVALID_FILE_ATTRIBUTES;
1468 if (flags & VIFF_FORCEINSTALL) {
1469 if (tmpfile[0]) {
1470 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
1471 tmplast = strlen(pdest)+1;
1472 attr = GetFileAttributesA(tmpfn);
1473 /* if it exists, it has been copied by the call before.
1474 * we jump over the copy part...
1475 */
1476 }
1477 }
1478 if (attr == INVALID_FILE_ATTRIBUTES) {
1479 char *s;
1480
1481 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
1482 s=strrchr(tmpfn,'\\');
1483 if (s)
1484 tmplast = s-tmpfn;
1485 else
1486 tmplast = 0;
1487 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
1488 if (hfdst == HFILE_ERROR) {
1489 LZClose(hfsrc);
1490 return VIF_CANNOTCREATE; /* | translated dos error */
1491 }
1492 ret = LZCopy(hfsrc,hfdst);
1493 _lclose(hfdst);
1494 if (ret < 0) {
1495 /* translate LZ errors into VIF_xxx */
1496 switch (ret) {
1497 case LZERROR_BADINHANDLE:
1498 case LZERROR_READ:
1499 case LZERROR_BADVALUE:
1500 case LZERROR_UNKNOWNALG:
1501 xret = VIF_CANNOTREADSRC;
1502 break;
1503 case LZERROR_BADOUTHANDLE:
1504 case LZERROR_WRITE:
1505 xret = VIF_OUTOFSPACE;
1506 break;
1507 case LZERROR_GLOBALLOC:
1508 case LZERROR_GLOBLOCK:
1509 xret = VIF_OUTOFMEMORY;
1510 break;
1511 default: /* unknown error, should not happen */
1512 FIXME("Unknown LZCopy error %d, ignoring.\n", ret);
1513 xret = 0;
1514 break;
1515 }
1516 if (xret) {
1517 LZClose(hfsrc);
1518 return xret;
1519 }
1520 }
1521 }
1522 if (!(flags & VIFF_FORCEINSTALL)) {
1523 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
1524 buf1 = _fetch_versioninfo(destfn,&destvffi);
1525 if (buf1) {
1526 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
1527 if (buf2) {
1528 char *tbuf1,*tbuf2;
1529 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
1530 UINT len1,len2;
1531
1532 len1=len2=40;
1533
1534 /* compare file versions */
1535 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
1536 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
1537 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
1538 )
1539 )
1540 xret |= VIF_MISMATCH|VIF_SRCOLD;
1541 /* compare filetypes and filesubtypes */
1542 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
1543 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
1544 )
1545 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
1546 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
1547 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
1548 ) {
1549 /* Do something with tbuf1 and tbuf2
1550 * generates DIFFLANG|MISMATCH
1551 */
1552 }
1553 HeapFree(GetProcessHeap(), 0, buf2);
1554 } else
1555 xret=VIF_MISMATCH|VIF_SRCOLD;
1556 HeapFree(GetProcessHeap(), 0, buf1);
1557 }
1558 }
1559 if (xret) {
1560 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
1561 xret|=VIF_BUFFTOOSMALL;
1562 DeleteFileA(tmpfn);
1563 } else {
1564 strcpy(tmpfile,tmpfn+tmplast);
1565 *tmpfilelen = strlen(tmpfn+tmplast)+1;
1566 xret|=VIF_TEMPFILE;
1567 }
1568 } else {
1569 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
1570 if (!DeleteFileA(destfn)) {
1571 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
1572 DeleteFileA(tmpfn);
1573 LZClose(hfsrc);
1574 return xret;
1575 }
1576 if ((!(flags & VIFF_DONTDELETEOLD)) &&
1577 curdir &&
1578 *curdir &&
1579 lstrcmpiA(curdir,pdest)
1580 ) {
1581 char curfn[260];
1582
1583 sprintf(curfn,"%s\\%s",curdir,destfilename);
1584 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
1585 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
1586 if (!DeleteFileA(curfn))
1587 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
1588 }
1589 }
1590 if (!MoveFileA(tmpfn,destfn)) {
1591 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
1592 DeleteFileA(tmpfn);
1593 }
1594 }
1595 LZClose(hfsrc);
1596 return xret;
1597 }
1598
1599
1600 /******************************************************************************
1601 * VerInstallFileW [VERSION.@]
1602 */
1603 DWORD WINAPI VerInstallFileW(
1604 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
1605 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
1606 {
1607 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
1608 DWORD ret = 0;
1609 UINT len;
1610
1611 if (srcfilename)
1612 {
1613 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
1614 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
1615 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
1616 else
1617 ret = VIF_OUTOFMEMORY;
1618 }
1619 if (srcdir && !ret)
1620 {
1621 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
1622 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
1623 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
1624 else
1625 ret = VIF_OUTOFMEMORY;
1626 }
1627 if (destfilename && !ret)
1628 {
1629 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
1630 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
1631 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
1632 else
1633 ret = VIF_OUTOFMEMORY;
1634 }
1635 if (destdir && !ret)
1636 {
1637 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
1638 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
1639 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
1640 else
1641 ret = VIF_OUTOFMEMORY;
1642 }
1643 if (curdir && !ret)
1644 {
1645 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
1646 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
1647 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
1648 else
1649 ret = VIF_OUTOFMEMORY;
1650 }
1651 if (!ret)
1652 {
1653 len = *tmpfilelen * sizeof(WCHAR);
1654 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
1655 if (!wtmpf)
1656 ret = VIF_OUTOFMEMORY;
1657 }
1658 if (!ret)
1659 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
1660 if (!ret)
1661 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
1662 else if (ret & VIF_BUFFTOOSMALL)
1663 *tmpfilelen = len; /* FIXME: not correct */
1664
1665 HeapFree( GetProcessHeap(), 0, wsrcf );
1666 HeapFree( GetProcessHeap(), 0, wsrcd );
1667 HeapFree( GetProcessHeap(), 0, wdestf );
1668 HeapFree( GetProcessHeap(), 0, wdestd );
1669 HeapFree( GetProcessHeap(), 0, wtmpf );
1670 HeapFree( GetProcessHeap(), 0, wcurd );
1671 return ret;
1672 }