78f88373647dfda9de27c4cbe9847bc2cd00520f
[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 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 }