wininet: Fix an off-by-one error in InternetCreateUrlW.
[wine] / dlls / version / info.c
1 /*
2  * Implementation of VERSION.DLL - Version Info access
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winreg.h"
32 #include "winver.h"
33 #include "winternl.h"
34 #include "wine/winuser16.h"
35 #include "wine/unicode.h"
36 #include "winerror.h"
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(ver);
40
41 /******************************************************************************
42  *
43  *   This function will print via standard TRACE, debug info regarding
44  *   the file info structure vffi.
45  *      15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu)
46  *      Added this function to clean up the code.
47  *
48  *****************************************************************************/
49 static void print_vffi_debug(VS_FIXEDFILEINFO *vffi)
50 {
51     BOOL    versioned_printer = FALSE;
52
53     if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV))
54     {
55         if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER)
56             /* this is documented for newer w2k Drivers and up */
57             versioned_printer = TRUE;
58         else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) &&
59                  (vffi->dwFileVersionMS != vffi->dwProductVersionMS) &&
60                  (vffi->dwFileVersionMS > 0) &&
61                  (vffi->dwFileVersionMS <= 3) )
62             /* found this on NT 3.51, NT4.0 and old w2k Drivers */
63             versioned_printer = TRUE;
64     }
65
66     TRACE("structversion=%u.%u, ",
67             HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion));
68     if(versioned_printer)
69     {
70         WORD mode = LOWORD(vffi->dwFileVersionMS);
71         WORD ver_rev = HIWORD(vffi->dwFileVersionLS);
72         TRACE("fileversion=%lu.%u.%u.%u (%s.major.minor.release), ",
73             (vffi->dwFileVersionMS),
74             HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS),
75             (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") );
76     }
77     else
78     {
79         TRACE("fileversion=%u.%u.%u.%u, ",
80             HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS),
81             HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS));
82     }
83     TRACE("productversion=%u.%u.%u.%u\n",
84           HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS),
85           HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS));
86
87     TRACE("flagmask=0x%lx, flags=0x%lx %s%s%s%s%s%s\n",
88           vffi->dwFileFlagsMask, vffi->dwFileFlags,
89           (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "",
90           (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "",
91           (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "",
92           (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "",
93           (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "",
94           (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : "");
95
96     TRACE("(");
97
98     TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS));
99
100     switch (vffi->dwFileOS&0xFFFF0000)
101     {
102     case VOS_DOS:TRACE("DOS,");break;
103     case VOS_OS216:TRACE("OS/2-16,");break;
104     case VOS_OS232:TRACE("OS/2-32,");break;
105     case VOS_NT:TRACE("NT,");break;
106     case VOS_UNKNOWN:
107     default:
108         TRACE("UNKNOWN(0x%lx),",vffi->dwFileOS&0xFFFF0000);break;
109     }
110
111     switch (LOWORD(vffi->dwFileOS))
112     {
113     case VOS__BASE:TRACE("BASE");break;
114     case VOS__WINDOWS16:TRACE("WIN16");break;
115     case VOS__WINDOWS32:TRACE("WIN32");break;
116     case VOS__PM16:TRACE("PM16");break;
117     case VOS__PM32:TRACE("PM32");break;
118     default:
119         TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break;
120     }
121
122     TRACE(")\n");
123
124     switch (vffi->dwFileType)
125     {
126     case VFT_APP:TRACE("filetype=APP");break;
127     case VFT_DLL:
128         TRACE("filetype=DLL");
129         if(vffi->dwFileSubtype != 0)
130         {
131             if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver  */
132                 TRACE(",PRINTER");
133             TRACE(" (subtype=0x%lx)", vffi->dwFileSubtype);
134         }
135         break;
136     case VFT_DRV:
137         TRACE("filetype=DRV,");
138         switch(vffi->dwFileSubtype)
139         {
140         case VFT2_DRV_PRINTER:TRACE("PRINTER");break;
141         case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break;
142         case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break;
143         case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break;
144         case VFT2_DRV_MOUSE:TRACE("MOUSE");break;
145         case VFT2_DRV_NETWORK:TRACE("NETWORK");break;
146         case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break;
147         case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break;
148         case VFT2_DRV_SOUND:TRACE("SOUND");break;
149         case VFT2_DRV_COMM:TRACE("COMM");break;
150         case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break;
151         case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break;
152         case VFT2_UNKNOWN:
153         default:
154             TRACE("UNKNOWN(0x%lx)",vffi->dwFileSubtype);break;
155         }
156         break;
157     case VFT_FONT:
158         TRACE("filetype=FONT,");
159         switch (vffi->dwFileSubtype)
160         {
161         case VFT2_FONT_RASTER:TRACE("RASTER");break;
162         case VFT2_FONT_VECTOR:TRACE("VECTOR");break;
163         case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break;
164         default:TRACE("UNKNOWN(0x%lx)",vffi->dwFileSubtype);break;
165         }
166         break;
167     case VFT_VXD:TRACE("filetype=VXD");break;
168     case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break;
169     case VFT_UNKNOWN:
170     default:
171         TRACE("filetype=Unknown(0x%lx)",vffi->dwFileType);break;
172     }
173
174     TRACE("\n");
175     TRACE("filedate=0x%lx.0x%lx\n",vffi->dwFileDateMS,vffi->dwFileDateLS);
176 }
177
178 /***********************************************************************
179  * Version Info Structure
180  */
181
182 typedef struct
183 {
184     WORD  wLength;
185     WORD  wValueLength;
186     CHAR  szKey[1];
187 #if 0   /* variable length structure */
188     /* DWORD aligned */
189     BYTE  Value[];
190     /* DWORD aligned */
191     VS_VERSION_INFO_STRUCT16 Children[];
192 #endif
193 } VS_VERSION_INFO_STRUCT16;
194
195 typedef struct
196 {
197     WORD  wLength;
198     WORD  wValueLength;
199     WORD  wType;
200     WCHAR szKey[1];
201 #if 0   /* variable length structure */
202     /* DWORD aligned */
203     BYTE  Value[];
204     /* DWORD aligned */
205     VS_VERSION_INFO_STRUCT32 Children[];
206 #endif
207 } VS_VERSION_INFO_STRUCT32;
208
209 #define VersionInfoIs16( ver ) \
210     ( ((VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' )
211
212 #define DWORD_ALIGN( base, ptr ) \
213     ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) )
214
215 #define VersionInfo16_Value( ver )  \
216     DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 )
217 #define VersionInfo32_Value( ver )  \
218     DWORD_ALIGN( (ver), (ver)->szKey + strlenW((ver)->szKey) + 1 )
219
220 #define VersionInfo16_Children( ver )  \
221     (VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \
222                            ( ( (ver)->wValueLength + 3 ) & ~3 ) )
223 #define VersionInfo32_Children( ver )  \
224     (VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \
225                            ( ( (ver)->wValueLength * \
226                                ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
227
228 #define VersionInfo16_Next( ver ) \
229     (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
230 #define VersionInfo32_Next( ver ) \
231     (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
232
233 /***********************************************************************
234  *           VERSION_GetFileVersionInfo_PE             [internal]
235  *
236  *    NOTE: returns size of the PE VERSION resource or 0xFFFFFFFF
237  *    in the case the file is a PE module, but VERSION_INFO not found.
238  */
239 static DWORD VERSION_GetFileVersionInfo_PE( LPCWSTR filename, DWORD datasize, LPVOID data )
240 {
241     VS_FIXEDFILEINFO *vffi;
242     DWORD len;
243     BYTE *buf;
244     HMODULE hModule;
245     HRSRC hRsrc;
246     HGLOBAL hMem;
247
248     TRACE("%s\n", debugstr_w(filename));
249
250     hModule = GetModuleHandleW(filename);
251     if(!hModule)
252         hModule = LoadLibraryExW(filename, 0, LOAD_LIBRARY_AS_DATAFILE);
253     else
254         hModule = LoadLibraryExW(filename, 0, 0);
255     if(!hModule)
256     {
257         WARN("Could not load %s\n", debugstr_w(filename));
258         return 0;
259     }
260     hRsrc = FindResourceW(hModule,
261                           MAKEINTRESOURCEW(VS_VERSION_INFO),
262                           MAKEINTRESOURCEW(VS_FILE_INFO));
263     if(!hRsrc)
264     {
265         WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_w(filename));
266         FreeLibrary(hModule);
267         return 0xFFFFFFFF;
268     }
269     len = SizeofResource(hModule, hRsrc);
270     hMem = LoadResource(hModule, hRsrc);
271     if(!hMem)
272     {
273         WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_w(filename));
274         FreeLibrary(hModule);
275         return 0xFFFFFFFF;
276     }
277     buf = LockResource(hMem);
278
279     vffi = (VS_FIXEDFILEINFO *)VersionInfo32_Value( (VS_VERSION_INFO_STRUCT32 *)buf );
280
281     if ( vffi->dwSignature != VS_FFI_SIGNATURE )
282     {
283         WARN("vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n",
284                    vffi->dwSignature, VS_FFI_SIGNATURE );
285         len = 0xFFFFFFFF;
286         goto END;
287     }
288
289     if ( TRACE_ON(ver) )
290         print_vffi_debug( vffi );
291
292     if(data)
293     {
294         if(datasize < len)
295             len = datasize; /* truncate data */
296         if(len)
297             memcpy(data, buf, len);
298         else
299             len = 0xFFFFFFFF;
300     }
301 END:
302     FreeResource(hMem);
303     FreeLibrary(hModule);
304
305     return len;
306 }
307
308 /***********************************************************************
309  *           VERSION_GetFileVersionInfo_16             [internal]
310  *
311  *    NOTE: returns size of the 16-bit VERSION resource or 0xFFFFFFFF
312  *    in the case the file exists, but VERSION_INFO not found.
313  */
314 static DWORD VERSION_GetFileVersionInfo_16( LPCSTR filename, DWORD datasize, LPVOID data )
315 {
316     VS_FIXEDFILEINFO *vffi;
317     DWORD len, offset;
318     BYTE *buf;
319     HMODULE16 hModule;
320     HRSRC16 hRsrc;
321     HGLOBAL16 hMem;
322     char dllname[20], owner[20], *p;
323     const char *basename;
324     BOOL is_builtin = FALSE;
325
326     TRACE("%s\n", debugstr_a(filename));
327
328     /* strip path information */
329
330     basename = filename;
331     if (basename[0] && basename[1] == ':') basename += 2;  /* strip drive specification */
332     if ((p = strrchr( basename, '\\' ))) basename = p + 1;
333     if ((p = strrchr( basename, '/' ))) basename = p + 1;
334
335     if (strlen(basename) < sizeof(dllname)-4)
336     {
337         int file_exists;
338
339         strcpy( dllname, basename );
340         p = strrchr( dllname, '.' );
341         if (!p) strcat( dllname, ".dll" );
342         for (p = dllname; *p; p++) if (*p >= 'A' && *p <= 'Z') *p += 32;
343
344         if (wine_dll_get_owner( dllname, owner, sizeof(owner), &file_exists ) == 0)
345             is_builtin = TRUE;
346     }
347
348     /* first try without loading a 16-bit module */
349     if (is_builtin)
350         len = 0;
351     else
352         len = GetFileResourceSize16( filename,
353                                      MAKEINTRESOURCEA(VS_FILE_INFO),
354                                      MAKEINTRESOURCEA(VS_VERSION_INFO),
355                                      &offset );
356     if (len)
357     {
358         if (!data) return len;
359
360         len = GetFileResource16( filename,
361                                  MAKEINTRESOURCEA(VS_FILE_INFO),
362                                  MAKEINTRESOURCEA(VS_VERSION_INFO),
363                                  offset, datasize, data );
364         if (len)
365         {
366             vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data );
367
368             if ( vffi->dwSignature == VS_FFI_SIGNATURE )
369             {
370                 if ( ((VS_VERSION_INFO_STRUCT16 *)data)->wLength < len )
371                     len = ((VS_VERSION_INFO_STRUCT16 *)data)->wLength;
372
373                 if ( TRACE_ON(ver) )
374                     print_vffi_debug( vffi );
375
376                 return len;
377             }
378         }
379     }
380
381     /* this might be a builtin 16-bit module */
382     hModule = LoadLibrary16(filename);
383     if(hModule < 32)
384     {
385         WARN("Could not load %s\n", debugstr_a(filename));
386         return 0;
387     }
388     hRsrc = FindResource16(hModule,
389                           MAKEINTRESOURCEA(VS_VERSION_INFO),
390                           MAKEINTRESOURCEA(VS_FILE_INFO));
391     if(!hRsrc)
392     {
393         WARN("Could not find VS_VERSION_INFO in %s\n", debugstr_a(filename));
394         FreeLibrary16(hModule);
395         return 0xFFFFFFFF;
396     }
397     len = SizeofResource16(hModule, hRsrc);
398     hMem = LoadResource16(hModule, hRsrc);
399     if(!hMem)
400     {
401         WARN("Could not load VS_VERSION_INFO from %s\n", debugstr_a(filename));
402         FreeLibrary16(hModule);
403         return 0xFFFFFFFF;
404     }
405     buf = LockResource16(hMem);
406
407     if(!VersionInfoIs16(buf))
408     {
409         len = 0xFFFFFFFF;
410         goto END;
411     }
412
413     vffi = (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)buf );
414
415     if ( vffi->dwSignature != VS_FFI_SIGNATURE )
416     {
417         WARN("vffi->dwSignature is 0x%08lx, but not 0x%08lx!\n",
418                    vffi->dwSignature, VS_FFI_SIGNATURE );
419         len = 0xFFFFFFFF;
420         goto END;
421     }
422
423     if ( TRACE_ON(ver) )
424         print_vffi_debug( vffi );
425
426     if(data)
427     {
428         if(datasize < len)
429             len = datasize; /* truncate data */
430         if(len)
431             memcpy(data, buf, len);
432         else
433             len = 0xFFFFFFFF;
434     }
435 END:
436     FreeResource16(hMem);
437     FreeLibrary16(hModule);
438
439     return len;
440 }
441
442 /***********************************************************************
443  *           GetFileVersionInfoSizeW         [VERSION.@]
444  */
445 DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle )
446 {
447     DWORD len;
448
449     TRACE("(%s,%p)\n", debugstr_w(filename), handle );
450
451     if (handle) *handle = 0;
452
453     if (!filename)
454     {
455         SetLastError(ERROR_INVALID_PARAMETER);
456         return 0;
457     }
458     if (!*filename)
459     {
460         SetLastError(ERROR_BAD_PATHNAME);
461         return 0;
462     }
463
464     len = VERSION_GetFileVersionInfo_PE(filename, 0, NULL);
465     /* 0xFFFFFFFF means: file is a PE module, but VERSION_INFO not found */
466     if(len == 0xFFFFFFFF)
467     {
468         SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
469         return 0;
470     }
471
472     if (!len)
473     {
474         LPSTR filenameA;
475
476         len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL );
477         filenameA = HeapAlloc( GetProcessHeap(), 0, len );
478         WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL );
479
480         len = VERSION_GetFileVersionInfo_16(filenameA, 0, NULL);
481         HeapFree( GetProcessHeap(), 0, filenameA );
482         /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */
483         if (!len)
484         {
485             SetLastError(ERROR_FILE_NOT_FOUND);
486             return 0;
487         }
488         if (len == 0xFFFFFFFF)
489         {
490             SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
491             return 0;
492         }
493
494         /* We have a 16bit resource.
495          *
496          * XP/W2K/W2K3 uses a buffer which is more than the actual needed space:
497          *
498          * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4
499          *
500          * This extra buffer is used for ANSI to Unicode conversions in W-Calls.
501          * info->wLength should be the same as len. Currently it isn't but that
502          * doesn't seem to be a problem (len is bigger then info->wLength).
503          */
504          len = (len - sizeof(VS_FIXEDFILEINFO)) * 4;
505     }
506     else
507     {
508         /* We have a 32bit resource.
509          *
510          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
511          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
512          */
513          len = (len * 2) + 4;
514     }
515
516     SetLastError(0);
517     return len;
518 }
519
520 /***********************************************************************
521  *           GetFileVersionInfoSizeA         [VERSION.@]
522  */
523 DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle )
524 {
525     UNICODE_STRING filenameW;
526     DWORD retval;
527
528     TRACE("(%s,%p)\n", debugstr_a(filename), handle );
529
530     if(filename)
531         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
532     else
533         filenameW.Buffer = NULL;
534
535     retval = GetFileVersionInfoSizeW(filenameW.Buffer, handle);
536
537     RtlFreeUnicodeString(&filenameW);
538
539     return retval;
540 }
541
542 /***********************************************************************
543  *           GetFileVersionInfoW             [VERSION.@]
544  */
545 BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle,
546                                     DWORD datasize, LPVOID data )
547 {
548     DWORD len;
549     VS_VERSION_INFO_STRUCT32* vvis = (VS_VERSION_INFO_STRUCT32*)data;
550
551     TRACE("(%s,%ld,size=%ld,data=%p)\n",
552                 debugstr_w(filename), handle, datasize, data );
553
554     if (!data)
555     {
556         SetLastError(ERROR_INVALID_DATA);
557         return FALSE;
558     }
559     len = VERSION_GetFileVersionInfo_PE(filename, datasize, data);
560     /* 0xFFFFFFFF means: file is a PE module, but VERSION_INFO not found */
561     if (len == 0xFFFFFFFF)
562     {
563         SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
564         return FALSE;
565     }
566
567     if (!len)
568     {
569         LPSTR filenameA;
570
571         len = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL );
572         filenameA = HeapAlloc( GetProcessHeap(), 0, len );
573         WideCharToMultiByte( CP_ACP, 0, filename, -1, filenameA, len, NULL, NULL );
574
575         len = VERSION_GetFileVersionInfo_16(filenameA, datasize, data);
576         HeapFree( GetProcessHeap(), 0, filenameA );
577         /* 0xFFFFFFFF means: file exists, but VERSION_INFO not found */
578         if (!len || len == 0xFFFFFFFF)
579         {
580             SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
581             return FALSE;
582         }
583         /* We have a 16bit resource. */
584     }
585     else 
586     {
587         static const char signature[] = "FE2X";
588         DWORD bufsize = vvis->wLength + strlen(signature);
589         DWORD convbuf;
590  
591         /* We have a 32bit resource.
592          *
593          * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X"
594          * This extra buffer is used for Unicode to ANSI conversions in A-Calls
595          */
596
597         /* information is truncated to datasize bytes */
598         if (datasize >= bufsize)
599         {
600             convbuf = datasize - vvis->wLength;
601             memcpy( ((char*)(data))+vvis->wLength, signature, convbuf > 4 ? 4 : convbuf );
602         }
603     }
604
605     SetLastError(0);
606     return TRUE;
607 }
608
609 /***********************************************************************
610  *           GetFileVersionInfoA             [VERSION.@]
611  */
612 BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle,
613                                     DWORD datasize, LPVOID data )
614 {
615     UNICODE_STRING filenameW;
616     BOOL retval;
617
618     TRACE("(%s,%ld,size=%ld,data=%p)\n",
619                 debugstr_a(filename), handle, datasize, data );
620
621     if(filename)
622         RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
623     else
624         filenameW.Buffer = NULL;
625
626     retval = GetFileVersionInfoW(filenameW.Buffer, handle, datasize, data);
627
628     RtlFreeUnicodeString(&filenameW);
629
630     return retval;
631 }
632
633 /***********************************************************************
634  *           VersionInfo16_FindChild             [internal]
635  */
636 static VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( VS_VERSION_INFO_STRUCT16 *info,
637                                             LPCSTR szKey, UINT cbKey )
638 {
639     VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info );
640
641     while ((char *)child < (char *)info + info->wLength )
642     {
643         if ( !strncasecmp( child->szKey, szKey, cbKey ) )
644             return child;
645
646         if (!(child->wLength)) return NULL;
647         child = VersionInfo16_Next( child );
648     }
649
650     return NULL;
651 }
652
653 /***********************************************************************
654  *           VersionInfo32_FindChild             [internal]
655  */
656 static VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( VS_VERSION_INFO_STRUCT32 *info,
657                                             LPCWSTR szKey, UINT cbKey )
658 {
659     VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info );
660
661     while ((char *)child < (char *)info + info->wLength )
662     {
663         if ( !strncmpiW( child->szKey, szKey, cbKey ) )
664             return child;
665
666         child = VersionInfo32_Next( child );
667     }
668
669     return NULL;
670 }
671
672 /***********************************************************************
673  *           VersionInfo16_QueryValue              [internal]
674  *
675  *    Gets a value from a 16-bit NE resource
676  */
677 static BOOL WINAPI VersionInfo16_QueryValue( VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock,
678                                LPVOID *lplpBuffer, UINT *puLen )
679 {
680     while ( *lpSubBlock )
681     {
682         /* Find next path component */
683         LPCSTR lpNextSlash;
684         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
685             if ( *lpNextSlash == '\\' )
686                 break;
687
688         /* Skip empty components */
689         if ( lpNextSlash == lpSubBlock )
690         {
691             lpSubBlock++;
692             continue;
693         }
694
695         /* We have a non-empty component: search info for key */
696         info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
697         if ( !info ) return FALSE;
698
699         /* Skip path component */
700         lpSubBlock = lpNextSlash;
701     }
702
703     /* Return value */
704     *lplpBuffer = VersionInfo16_Value( info );
705     if (puLen)
706         *puLen = info->wValueLength;
707
708     return TRUE;
709 }
710
711 /***********************************************************************
712  *           VersionInfo32_QueryValue              [internal]
713  *
714  *    Gets a value from a 32-bit PE resource
715  */
716 static BOOL WINAPI VersionInfo32_QueryValue( VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock,
717                                LPVOID *lplpBuffer, UINT *puLen )
718 {
719     TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock));
720
721     while ( *lpSubBlock )
722     {
723         /* Find next path component */
724         LPCWSTR lpNextSlash;
725         for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ )
726             if ( *lpNextSlash == '\\' )
727                 break;
728
729         /* Skip empty components */
730         if ( lpNextSlash == lpSubBlock )
731         {
732             lpSubBlock++;
733             continue;
734         }
735
736         /* We have a non-empty component: search info for key */
737         info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock );
738         if ( !info ) return FALSE;
739
740         /* Skip path component */
741         lpSubBlock = lpNextSlash;
742     }
743
744     /* Return value */
745     *lplpBuffer = VersionInfo32_Value( info );
746     if (puLen)
747         *puLen = info->wValueLength;
748
749     return TRUE;
750 }
751
752 /***********************************************************************
753  *           VerQueryValueA              [VERSION.@]
754  */
755 BOOL WINAPI VerQueryValueA( LPVOID pBlock, LPSTR lpSubBlock,
756                                LPVOID *lplpBuffer, UINT *puLen )
757 {
758     static const char rootA[] = "\\";
759     static const char varfileinfoA[] = "\\VarFileInfo\\Translation";
760     VS_VERSION_INFO_STRUCT16 *info = (VS_VERSION_INFO_STRUCT16 *)pBlock;
761
762     TRACE("(%p,%s,%p,%p)\n",
763                 pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen );
764
765     if ( !VersionInfoIs16( info ) )
766     {
767         BOOL ret;
768         INT len;
769         LPWSTR lpSubBlockW;
770
771         len  = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0);
772         lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
773
774         if (!lpSubBlockW)
775             return FALSE;
776
777         MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len);
778
779         ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, puLen);
780
781         HeapFree(GetProcessHeap(), 0, lpSubBlockW);
782
783         if (ret && strcasecmp( lpSubBlock, rootA ) && strcasecmp( lpSubBlock, varfileinfoA ))
784         {
785             LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4;
786             DWORD pos = (LPSTR)*lplpBuffer - (LPSTR)pBlock;
787
788             len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)*lplpBuffer, -1,
789                                       lpBufferA + pos, info->wLength - pos, NULL, NULL);
790             *lplpBuffer = lpBufferA + pos;
791             *puLen = len;
792         }
793         return ret;
794     }
795
796     return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
797 }
798
799 /***********************************************************************
800  *           VerQueryValueW              [VERSION.@]
801  */
802 BOOL WINAPI VerQueryValueW( LPVOID pBlock, LPWSTR lpSubBlock,
803                                LPVOID *lplpBuffer, UINT *puLen )
804 {
805     static const WCHAR rootW[] = { '\\', 0 };
806     static const WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
807                                           '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
808
809     VS_VERSION_INFO_STRUCT32 *info = (VS_VERSION_INFO_STRUCT32 *)pBlock;
810
811     TRACE("(%p,%s,%p,%p)\n",
812                 pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen );
813
814     if ( VersionInfoIs16( info ) )
815     {
816         BOOL ret;
817         int len;
818         LPSTR lpSubBlockA;
819
820         len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL);
821         lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
822
823         if (!lpSubBlockA)
824             return FALSE;
825
826         WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL);
827
828         ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen);
829
830         HeapFree(GetProcessHeap(), 0, lpSubBlockA);
831
832         if (ret && strcmpiW( lpSubBlock, rootW ) && strcmpiW( lpSubBlock, varfileinfoW ))
833         {
834             LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength);
835             DWORD pos = (LPSTR)*lplpBuffer - (LPSTR)pBlock;
836             DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength;
837
838             len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*lplpBuffer, -1,
839                                       lpBufferW + pos, max/sizeof(WCHAR) - pos );
840             *lplpBuffer = lpBufferW + pos;
841             *puLen = len;
842         }
843         return ret;
844     }
845
846     return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen);
847 }