Fixed checking of special dll names.
[wine] / misc / version.c
1 /*
2  * Windows and DOS version functions
3  *
4  * Copyright 1997 Alexandre Julliard
5  * Copyright 1997 Marcus Meissner
6  * Copyright 1998 Patrik Stridvall
7  * Copyright 1998 Andreas Mohr
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 #include "config.h"
25 #include "wine/port.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "ntddk.h"
36 #include "wine/winbase16.h"
37 #include "module.h"
38 #include "wine/debug.h"
39 #include "winerror.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(ver);
42
43 typedef enum
44 {
45     WIN20,   /* Windows 2.0 */
46     WIN30,   /* Windows 3.0 */
47     WIN31,   /* Windows 3.1 */
48     WIN95,   /* Windows 95 */
49     WIN98,   /* Windows 98 */
50     WINME,   /* Windows Me */
51     NT351,   /* Windows NT 3.51 */
52     NT40,    /* Windows NT 4.0 */
53     NT2K,    /* Windows 2000 */
54     WINXP,   /* Windows XP */
55     NB_WINDOWS_VERSIONS
56 } WINDOWS_VERSION;
57
58 typedef struct
59 {
60     LONG             getVersion16;
61     LONG             getVersion32;
62     OSVERSIONINFOEXA getVersionEx;
63 } VERSION_DATA;
64
65 /* FIXME: compare values below with original and fix.
66  * An *excellent* win9x version page (ALL versions !)
67  * can be found at members.aol.com/axcel216/ver.htm */
68 static VERSION_DATA VersionData[NB_WINDOWS_VERSIONS] =
69 {
70     /* WIN20 FIXME: verify values */
71     {
72         MAKELONG( 0x0002, 0x0303 ), /* assume DOS 3.3 */
73         MAKELONG( 0x0002, 0x8000 ),
74         {
75             sizeof(OSVERSIONINFOA), 2, 0, 0,
76             VER_PLATFORM_WIN32s, "Win32s 1.3",
77             0, 0, 0, 0, 0
78         }
79     },
80     /* WIN30 FIXME: verify values */
81     {
82         MAKELONG( 0x0003, 0x0500 ), /* assume DOS 5.00 */
83         MAKELONG( 0x0003, 0x8000 ),
84         {
85             sizeof(OSVERSIONINFOA), 3, 0, 0,
86             VER_PLATFORM_WIN32s, "Win32s 1.3",
87             0, 0, 0, 0, 0
88         }
89     },
90     /* WIN31 */
91     {
92         MAKELONG( 0x0a03, 0x0616 ), /* DOS 6.22 */
93         MAKELONG( 0x0a03, 0x8000 ),
94         {
95             sizeof(OSVERSIONINFOA), 3, 10, 0,
96             VER_PLATFORM_WIN32s, "Win32s 1.3",
97             0, 0, 0, 0, 0
98         }
99     },
100     /* WIN95 */
101     {
102         0x07005F03,
103         0xC0000004,
104         {
105             /* Win95:       4, 0, 0x40003B6, ""
106              * Win95sp1:    4, 0, 0x40003B6, " A " (according to doc)
107              * Win95osr2:   4, 0, 0x4000457, " B " (according to doc)
108              * Win95osr2.1: 4, 3, 0x40304BC, " B " (according to doc)
109              * Win95osr2.5: 4, 3, 0x40304BE, " C " (according to doc)
110              * Win95a/b can be discerned via regkey SubVersionNumber
111              * See also:
112              * http://support.microsoft.com/support/kb/articles/q158/2/38.asp
113              */
114             sizeof(OSVERSIONINFOA), 4, 0, 0x40003B6,
115             VER_PLATFORM_WIN32_WINDOWS, "",
116             0, 0, 0, 0, 0
117         }
118     },
119     /* WIN98 (second edition) */
120     {
121         0x070A5F03,
122         0xC0000A04,
123         {
124             /* Win98:   4, 10, 0x40A07CE, " "   4.10.1998
125              * Win98SE: 4, 10, 0x40A08AE, " A " 4.10.2222
126              */
127             sizeof(OSVERSIONINFOA), 4, 10, 0x40A08AE,
128             VER_PLATFORM_WIN32_WINDOWS, " A ",
129             0, 0, 0, 0, 0
130         }
131     },
132     /* WINME */
133     {
134         0x08005F03,
135         0xC0005A04,
136         {
137             sizeof(OSVERSIONINFOA), 4, 90, 0x45A0BB8,
138             VER_PLATFORM_WIN32_WINDOWS, " ",
139             0, 0, 0, 0, 0
140         }
141     },
142     /* NT351 */
143     {
144         0x05000A03,
145         0x04213303,
146         {
147             sizeof(OSVERSIONINFOA), 3, 51, 0x421,
148             VER_PLATFORM_WIN32_NT, "Service Pack 2",
149             0, 0, 0, 0, 0
150         }
151     },
152     /* NT40 */
153     {
154         0x05000A03,
155         0x05650004,
156         {
157             sizeof(OSVERSIONINFOA), 4, 0, 0x565,
158             VER_PLATFORM_WIN32_NT, "Service Pack 6",
159             6, 0, 0, VER_NT_WORKSTATION, 0
160         }
161     },
162     /* NT2K */
163     {
164         0x05005F03,
165         0x08930005,
166         {
167             sizeof(OSVERSIONINFOA), 5, 0, 0x893,
168             VER_PLATFORM_WIN32_NT, "Service Pack 2",
169             2, 0, 0, VER_NT_WORKSTATION, 30 /* FIXME: Great, a reserved field with a value! */
170         }
171     },
172     /* WINXP */
173     {
174         0x05005F03, /* Assuming DOS 5 like the other NT */
175         0x0A280105,
176         {
177             sizeof(OSVERSIONINFOA), 5, 1, 0xA28,
178             VER_PLATFORM_WIN32_NT, "",
179             0, 0, 0, VER_NT_WORKSTATION, 0 /* FIXME: Verify last 5 values */
180         }
181     }
182 };
183
184 static const char *WinVersionNames[NB_WINDOWS_VERSIONS] =
185 { /* no spaces in here ! */
186     "win20",
187     "win30",
188     "win31",
189     "win95",
190     "win98",
191     "winme",
192     "nt351",
193     "nt40",
194     "win2000,win2k,nt2k,nt2000",
195     "winxp"
196 };
197
198 /* if one of the following dlls is importing ntdll the windows
199 version autodetection switches wine to unicode (nt 3.51 or 4.0) */
200 static char * special_dlls[] =
201 {
202         "comdlg32.dll",
203         "comctl32.dll",
204         "shell32.dll",
205         "ole32.dll",
206         "rpcrt4.dll",
207         NULL
208 };
209
210 /* the current version has not been autodetected but forced via cmdline */
211 static BOOL versionForced = FALSE;
212 static WINDOWS_VERSION forcedWinVersion = WIN31; /* init value irrelevant */
213
214 /**********************************************************************
215  *         VERSION_ParseWinVersion
216  */
217 static void VERSION_ParseWinVersion( const char *arg )
218 {
219     int i, len;
220     const char *pCurr, *p;
221     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
222     {
223         pCurr = WinVersionNames[i];
224         /* iterate through all winver aliases separated by comma */
225         do {
226             p = strchr(pCurr, ',');
227             len = p ? (int)p - (int)pCurr : strlen(pCurr);
228             if ( (!strncmp( pCurr, arg, len )) && (arg[len] == '\0') )
229             {
230                 forcedWinVersion = (WINDOWS_VERSION)i;
231                 versionForced = TRUE;
232                 return;
233             }
234             pCurr = p+1;
235         } while (p);
236     }
237     MESSAGE("Invalid Windows version value '%s' specified in config file.\n", arg );
238     MESSAGE("Valid versions are:" );
239     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
240     {
241         /* only list the first, "official" alias in case of aliases */
242         pCurr = WinVersionNames[i];
243         p = strchr(pCurr, ',');
244         len = (p) ? (int)p - (int)pCurr : strlen(pCurr);
245
246         MESSAGE(" '%.*s'%c", len, pCurr,
247                 (i == NB_WINDOWS_VERSIONS - 1) ? '\n' : ',' );
248     }
249     ExitProcess(1);
250 }
251
252
253 /**********************************************************************
254  *         VERSION_ParseDosVersion
255  */
256 static void VERSION_ParseDosVersion( const char *arg )
257 {
258     int hi, lo;
259     if (sscanf( arg, "%d.%d", &hi, &lo ) == 2)
260     {
261         VersionData[WIN31].getVersion16 =
262             MAKELONG(LOWORD(VersionData[WIN31].getVersion16),
263                      (hi<<8) + lo);
264     }
265     else
266     {
267         MESSAGE("Wrong format for DOS version in config file. Use \"x.xx\"\n");
268         ExitProcess(1);
269     }
270 }
271
272
273 /**********************************************************************
274  *         VERSION_Init
275  */
276 static void VERSION_Init(void)
277 {
278     HKEY hkey, appkey;
279     DWORD count, type;
280     BOOL got_win_ver = FALSE, got_dos_ver = FALSE;
281     char buffer[MAX_PATH+16], *appname, *p;
282     static BOOL init_done;
283
284     if (init_done) return;
285     if (!GetModuleFileNameA( 0, buffer, MAX_PATH ))
286     {
287         WARN( "could not get module file name\n" );
288         return;
289     }
290     init_done = TRUE;
291     appname = buffer;
292     if ((p = strrchr( appname, '/' ))) appname = p + 1;
293     if ((p = strrchr( appname, '\\' ))) appname = p + 1;
294
295     if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &hkey ))
296     {
297         /* open AppDefaults\\appname\\Version key */
298         strcat( appname, "\\Version" );
299         if (!RegOpenKeyA( hkey, appname, &appkey ))
300         {
301             count = sizeof(buffer);
302             if (!RegQueryValueExA( appkey, "Windows", NULL, &type, buffer, &count ))
303             {
304                 VERSION_ParseWinVersion( buffer );
305                 TRACE( "got app win version %s\n", WinVersionNames[forcedWinVersion] );
306                 got_win_ver = TRUE;
307             }
308             count = sizeof(buffer);
309             if (!RegQueryValueExA( appkey, "DOS", NULL, &type, buffer, &count ))
310             {
311                 VERSION_ParseDosVersion( buffer );
312                 TRACE( "got app dos version %lx\n", VersionData[WIN31].getVersion16 );
313                 got_dos_ver = TRUE;
314             }
315             RegCloseKey( appkey );
316         }
317         RegCloseKey( hkey );
318     }
319
320     if (got_win_ver && got_dos_ver) return;
321
322     if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\Version", &hkey ))
323     {
324         if (!got_win_ver)
325         {
326             count = sizeof(buffer);
327             if (!RegQueryValueExA( hkey, "Windows", NULL, &type, buffer, &count ))
328             {
329                 VERSION_ParseWinVersion( buffer );
330                 TRACE( "got default win version %s\n", WinVersionNames[forcedWinVersion] );
331             }
332         }
333         if (!got_dos_ver)
334         {
335             count = sizeof(buffer);
336             if (!RegQueryValueExA( hkey, "DOS", NULL, &type, buffer, &count ))
337             {
338                 VERSION_ParseDosVersion( buffer );
339                 TRACE( "got default dos version %lx\n", VersionData[WIN31].getVersion16 );
340             }
341         }
342         RegCloseKey( hkey );
343     }
344 }
345
346
347 /**********************************************************************
348  *      VERSION_GetSystemDLLVersion
349  *
350  * This function tries to figure out if a given (native) dll comes from
351  * win95/98 or winnt. Since all values in the OptionalHeader are not a
352  * usable hint, we test if a dll imports the ntdll.
353  * This is at least working for all system dlls like comctl32, comdlg32 and
354  * shell32.
355  * If you have a better idea to figure this out...
356  */
357 static DWORD VERSION_GetSystemDLLVersion( HMODULE hmod )
358 {
359     DWORD size;
360     IMAGE_IMPORT_DESCRIPTOR *pe_imp;
361
362     if ((pe_imp = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size )))
363     {
364         for ( ; pe_imp->Name; pe_imp++)
365         {
366             char * name = (char *)hmod + (unsigned int)pe_imp->Name;
367             TRACE("%s\n", name);
368
369             if (!strncasecmp(name, "ntdll", 5))
370             {
371               switch(RtlImageNtHeader(hmod)->OptionalHeader.MajorOperatingSystemVersion) {
372                   case 3:
373                           MESSAGE("WARNING: very old native DLL (NT 3.x) used, might cause instability.\n");
374                           return NT351;
375                   case 4: return NT40;
376                   case 5: return NT2K;
377                   case 6: return WINXP;
378                   default:
379                           FIXME("Unknown DLL OS version, please report !!\n");
380                           return WINXP;
381               }
382             }
383         }
384     }
385     return WIN95;
386 }
387 /**********************************************************************
388  *      VERSION_GetLinkedDllVersion
389  *
390  * Some version data (not reliable!):
391  * linker/OS/image/subsys
392  *
393  * x.xx/1.00/0.00/3.10  Win32s          (any version ?)
394  * 2.39/1.00/0.00/3.10  Win32s          freecell.exe (any version)
395  * 2.50/1.00/4.00/4.00  Win32s 1.30     winhlp32.exe
396  * 2.60/3.51/3.51/3.51  NT351SP5        system dlls
397  * 2.60/3.51/3.51/4.00  NT351SP5        comctl32 dll
398  * 2.xx/1.00/0.00/4.00  Win95           system files
399  * x.xx/4.00/0.00/4.00  Win95           most applications
400  * 3.10/4.00/0.00/4.00  Win98           notepad
401  * x.xx/5.00/5.00/4.00  Win98           system dlls (e.g. comctl32.dll)
402  * x.xx/4.00/4.00/4.00  NT 4            most apps
403  * 5.12/5.00/5.00/4.00  NT4+IE5         comctl32.dll
404  * 5.12/5.00/5.00/4.00  Win98           calc
405  * x.xx/5.00/5.00/4.00  win95/win98/NT4 IE5 files
406  */
407 static DWORD VERSION_GetLinkedDllVersion(void)
408 {
409         WINE_MODREF *wm;
410         DWORD WinVersion = NB_WINDOWS_VERSIONS;
411         PIMAGE_OPTIONAL_HEADER ophd;
412         IMAGE_NT_HEADERS *nt;
413
414         /* First check the native dlls provided. These have to be
415         from one windows version */
416         for ( wm = MODULE_modref_list; wm; wm=wm->next )
417         {
418           nt = RtlImageNtHeader(wm->module);
419           ophd = &nt->OptionalHeader;
420
421           TRACE("%s: %02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
422             wm->modname,
423             ophd->MajorLinkerVersion, ophd->MinorLinkerVersion,
424             ophd->MajorOperatingSystemVersion, ophd->MinorOperatingSystemVersion,
425             ophd->MajorImageVersion, ophd->MinorImageVersion,
426             ophd->MajorSubsystemVersion, ophd->MinorSubsystemVersion);
427
428           /* test if it is an external (native) dll */
429           if (!(wm->flags & WINE_MODREF_INTERNAL))
430           {
431             int i;
432             for (i = 0; special_dlls[i]; i++)
433             {
434               /* test if it is a special dll */
435               if (!strcasecmp(wm->modname, special_dlls[i]))
436               {
437                 DWORD DllVersion = VERSION_GetSystemDLLVersion(wm->module);
438                 if (WinVersion == NB_WINDOWS_VERSIONS)
439                   WinVersion = DllVersion;
440                 else {
441                   if (WinVersion != DllVersion) {
442                     ERR("You mixed system DLLs from different windows versions! Expect a crash! (%s: expected version '%s', but is '%s')\n",
443                         wm->modname,
444                         VersionData[WinVersion].getVersionEx.szCSDVersion,
445                         VersionData[DllVersion].getVersionEx.szCSDVersion);
446                     return WIN20; /* this may let the exe exiting */
447                   }
448                 }
449                 break;
450               }
451             }
452           }
453         }
454
455         if(WinVersion != NB_WINDOWS_VERSIONS) return WinVersion;
456
457         /* we are using no external system dlls, look at the exe */
458         nt = RtlImageNtHeader(GetModuleHandleA(NULL));
459         ophd = &nt->OptionalHeader;
460
461         TRACE("%02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
462             ophd->MajorLinkerVersion, ophd->MinorLinkerVersion,
463             ophd->MajorOperatingSystemVersion, ophd->MinorOperatingSystemVersion,
464             ophd->MajorImageVersion, ophd->MinorImageVersion,
465             ophd->MajorSubsystemVersion, ophd->MinorSubsystemVersion);
466
467         /* special nt 3.51 */
468         if (3 == ophd->MajorOperatingSystemVersion && 51 == ophd->MinorOperatingSystemVersion)
469         {
470             return NT351;
471         }
472
473         /* the MajorSubsystemVersion is the only usable sign */
474         if (ophd->MajorSubsystemVersion < 4)
475         {
476           if ( ophd->MajorOperatingSystemVersion == 1
477             && ophd->MinorOperatingSystemVersion == 0)
478           {
479             return WIN31; /* win32s */
480           }
481
482           if (ophd->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
483             return NT351; /* FIXME: NT 3.1, not tested */
484           else
485             return WIN95;
486         }
487
488         return WIN95;
489 }
490
491 /**********************************************************************
492  *         VERSION_GetVersion
493  *
494  * WARNING !!!
495  * Don't call this function too early during the Wine init,
496  * as pdb->exe_modref (required by VERSION_GetImageVersion()) might still
497  * be NULL in such cases, which causes the winver to ALWAYS be detected
498  * as WIN31.
499  * And as we cache the winver once it has been determined, this is bad.
500  * This can happen much easier than you might think, as this function
501  * is called by EVERY GetVersion*() API !
502  *
503  */
504 static WINDOWS_VERSION VERSION_GetVersion(void)
505 {
506     static WORD winver = 0xffff;
507
508     if (winver == 0xffff) /* to be determined */
509     {
510         WINDOWS_VERSION retver;
511
512         VERSION_Init();
513         if (versionForced) /* user has overridden any sensible checks */
514             winver = forcedWinVersion;
515         else
516         {
517             retver = VERSION_GetLinkedDllVersion();
518
519             /* cache determined value, but do not store in case of WIN31 */
520             if (retver != WIN31) winver = retver;
521
522             return retver;
523         }
524     }
525
526     return winver;
527 }
528
529
530 /***********************************************************************
531  *         GetVersion   (KERNEL.3)
532  */
533 LONG WINAPI GetVersion16(void)
534 {
535     WINDOWS_VERSION ver = VERSION_GetVersion();
536     return VersionData[ver].getVersion16;
537 }
538
539
540 /***********************************************************************
541  *         GetVersion   (KERNEL32.@)
542  */
543 LONG WINAPI GetVersion(void)
544 {
545     WINDOWS_VERSION ver = VERSION_GetVersion();
546     return VersionData[ver].getVersion32;
547 }
548
549
550 /***********************************************************************
551  *         GetVersionEx   (KERNEL.149)
552  */
553 BOOL16 WINAPI GetVersionEx16(OSVERSIONINFO16 *v)
554 {
555     WINDOWS_VERSION ver = VERSION_GetVersion();
556     if (v->dwOSVersionInfoSize < sizeof(OSVERSIONINFO16))
557     {
558         WARN("wrong OSVERSIONINFO size from app\n");
559         SetLastError(ERROR_INSUFFICIENT_BUFFER);
560         return FALSE;
561     }
562     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
563     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
564     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
565     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
566     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
567     return TRUE;
568 }
569
570
571 /***********************************************************************
572  *         GetVersionExA   (KERNEL32.@)
573  */
574 BOOL WINAPI GetVersionExA(OSVERSIONINFOA *v)
575 {
576     WINDOWS_VERSION ver = VERSION_GetVersion();
577     LPOSVERSIONINFOEXA vex;
578
579     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA) &&
580         v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXA))
581     {
582         WARN("wrong OSVERSIONINFO size from app (got: %ld, expected: %d or %d)\n",
583                         v->dwOSVersionInfoSize, sizeof(OSVERSIONINFOA),
584                         sizeof(OSVERSIONINFOEXA));
585         SetLastError(ERROR_INSUFFICIENT_BUFFER);
586         return FALSE;
587     }
588     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
589     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
590     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
591     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
592     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
593     if(v->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)) {
594         vex = (LPOSVERSIONINFOEXA) v;
595         vex->wServicePackMajor = VersionData[ver].getVersionEx.wServicePackMajor;
596         vex->wServicePackMinor = VersionData[ver].getVersionEx.wServicePackMinor;
597         vex->wSuiteMask = VersionData[ver].getVersionEx.wSuiteMask;
598         vex->wProductType = VersionData[ver].getVersionEx.wProductType;
599     }
600     return TRUE;
601 }
602
603
604 /***********************************************************************
605  *         GetVersionExW   (KERNEL32.@)
606  */
607 BOOL WINAPI GetVersionExW(OSVERSIONINFOW *v)
608 {
609     WINDOWS_VERSION ver = VERSION_GetVersion();
610     LPOSVERSIONINFOEXW vex;
611
612     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOW) &&
613         v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXW))
614     {
615         WARN("wrong OSVERSIONINFO size from app (got: %ld, expected: %d or %d)\n",
616                         v->dwOSVersionInfoSize, sizeof(OSVERSIONINFOW),
617                         sizeof(OSVERSIONINFOEXW));
618         SetLastError(ERROR_INSUFFICIENT_BUFFER);
619         return FALSE;
620     }
621     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
622     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
623     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
624     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
625     MultiByteToWideChar( CP_ACP, 0, VersionData[ver].getVersionEx.szCSDVersion, -1,
626                          v->szCSDVersion, sizeof(v->szCSDVersion)/sizeof(WCHAR) );
627     if(v->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXW)) {
628         vex = (LPOSVERSIONINFOEXW) v;
629         vex->wServicePackMajor = VersionData[ver].getVersionEx.wServicePackMajor;
630         vex->wServicePackMinor = VersionData[ver].getVersionEx.wServicePackMinor;
631         vex->wSuiteMask = VersionData[ver].getVersionEx.wSuiteMask;
632         vex->wProductType = VersionData[ver].getVersionEx.wProductType;
633     }
634     return TRUE;
635 }
636
637
638 /******************************************************************************
639  *        VerifyVersionInfoA   (KERNEL32.@)
640  */
641 BOOL WINAPI VerifyVersionInfoA( LPOSVERSIONINFOEXA lpVersionInfo, DWORD dwTypeMask,
642                                 DWORDLONG dwlConditionMask)
643 {
644     OSVERSIONINFOEXW verW;
645
646     verW.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
647     verW.dwMajorVersion = lpVersionInfo->dwMajorVersion;
648     verW.dwMinorVersion = lpVersionInfo->dwMinorVersion;
649     verW.dwBuildNumber = lpVersionInfo->dwBuildNumber;
650     verW.dwPlatformId = lpVersionInfo->dwPlatformId;
651     verW.wServicePackMajor = lpVersionInfo->wServicePackMajor;
652     verW.wServicePackMinor = lpVersionInfo->wServicePackMinor;
653     verW.wSuiteMask = lpVersionInfo->wSuiteMask;
654     verW.wProductType = lpVersionInfo->wProductType;
655     verW.wReserved = lpVersionInfo->wReserved;
656
657     return VerifyVersionInfoW(&verW, dwTypeMask, dwlConditionMask);
658 }
659
660
661 /******************************************************************************
662  *        VerifyVersionInfoW   (KERNEL32.@)
663  */
664 BOOL WINAPI VerifyVersionInfoW( LPOSVERSIONINFOEXW lpVersionInfo, DWORD dwTypeMask,
665                                 DWORDLONG dwlConditionMask)
666 {
667     OSVERSIONINFOEXW ver;
668     BOOL res, error_set;
669
670     FIXME("(%p,%lu,%llx): Not all cases correctly implemented yet\n", lpVersionInfo, dwTypeMask, dwlConditionMask);
671     /* FIXME:
672         - Check the following special case on Windows (various versions):
673           o lp->wSuiteMask == 0 and ver.wSuiteMask != 0 and VER_AND/VER_OR
674           o lp->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXW)
675         - MSDN talks about some tests being impossible. Check what really happens.
676      */
677
678     ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
679     if(!GetVersionExW((LPOSVERSIONINFOW) &ver))
680         return FALSE;
681
682     res = TRUE;
683     error_set = FALSE;
684     if(!(dwTypeMask && dwlConditionMask)) {
685         res = FALSE;
686         SetLastError(ERROR_BAD_ARGUMENTS);
687         error_set = TRUE;
688     }
689     if(dwTypeMask & VER_PRODUCT_TYPE)
690         switch(dwlConditionMask >> 7*3 & 0x07) {
691             case VER_EQUAL:
692                 if(ver.wProductType != lpVersionInfo->wProductType)
693                     res = FALSE;
694                 break;
695             case VER_GREATER:
696                 if(ver.wProductType <= lpVersionInfo->wProductType)
697                     res = FALSE;
698                 break;
699             case VER_GREATER_EQUAL:
700                 if(ver.wProductType < lpVersionInfo->wProductType)
701                     res = FALSE;
702                 break;
703             case VER_LESS:
704                 if(ver.wProductType >= lpVersionInfo->wProductType)
705                     res = FALSE;
706                 break;
707             case VER_LESS_EQUAL:
708                 if(ver.wProductType > lpVersionInfo->wProductType)
709                     res = FALSE;
710                 break;
711             default:
712                 res = FALSE;
713                 SetLastError(ERROR_BAD_ARGUMENTS);
714                 error_set = TRUE;
715         }
716     if(dwTypeMask & VER_SUITENAME && res)
717         switch(dwlConditionMask >> 6*3 & 0x07) {
718             case VER_AND:
719                 if((lpVersionInfo->wSuiteMask & ver.wSuiteMask) != lpVersionInfo->wSuiteMask)
720                     res = FALSE;
721                 break;
722             case VER_OR:
723                 if(!(lpVersionInfo->wSuiteMask & ver.wSuiteMask) && lpVersionInfo->wSuiteMask)
724                     res = FALSE;
725                 break;
726             default:
727                 res = FALSE;
728                 SetLastError(ERROR_BAD_ARGUMENTS);
729                 error_set = TRUE;
730         }
731     if(dwTypeMask & VER_PLATFORMID && res)
732         switch(dwlConditionMask >> 3*3 & 0x07) {
733             case VER_EQUAL:
734                 if(ver.dwPlatformId != lpVersionInfo->dwPlatformId)
735                     res = FALSE;
736                 break;
737             case VER_GREATER:
738                 if(ver.dwPlatformId <= lpVersionInfo->dwPlatformId)
739                     res = FALSE;
740                 break;
741             case VER_GREATER_EQUAL:
742                 if(ver.dwPlatformId < lpVersionInfo->dwPlatformId)
743                     res = FALSE;
744                 break;
745             case VER_LESS:
746                 if(ver.dwPlatformId >= lpVersionInfo->dwPlatformId)
747                     res = FALSE;
748                 break;
749             case VER_LESS_EQUAL:
750                 if(ver.dwPlatformId > lpVersionInfo->dwPlatformId)
751                     res = FALSE;
752                 break;
753             default:
754                 res = FALSE;
755                 SetLastError(ERROR_BAD_ARGUMENTS);
756                 error_set = TRUE;
757         }
758     if(dwTypeMask & VER_BUILDNUMBER && res)
759         switch(dwlConditionMask >> 2*3 & 0x07) {
760             case VER_EQUAL:
761                 if(ver.dwBuildNumber != lpVersionInfo->dwBuildNumber)
762                     res = FALSE;
763                 break;
764             case VER_GREATER:
765                 if(ver.dwBuildNumber <= lpVersionInfo->dwBuildNumber)
766                     res = FALSE;
767                 break;
768             case VER_GREATER_EQUAL:
769                 if(ver.dwBuildNumber < lpVersionInfo->dwBuildNumber)
770                     res = FALSE;
771                 break;
772             case VER_LESS:
773                 if(ver.dwBuildNumber >= lpVersionInfo->dwBuildNumber)
774                     res = FALSE;
775                 break;
776             case VER_LESS_EQUAL:
777                 if(ver.dwBuildNumber > lpVersionInfo->dwBuildNumber)
778                     res = FALSE;
779                 break;
780             default:
781                 res = FALSE;
782                 SetLastError(ERROR_BAD_ARGUMENTS);
783                 error_set = TRUE;
784         }
785     if(dwTypeMask & VER_MAJORVERSION && res)
786         switch(dwlConditionMask >> 1*3 & 0x07) {
787             case VER_EQUAL:
788                 if(ver.dwMajorVersion != lpVersionInfo->dwMajorVersion)
789                     res = FALSE;
790                 break;
791             case VER_GREATER:
792                 if(ver.dwMajorVersion <= lpVersionInfo->dwMajorVersion)
793                     res = FALSE;
794                 break;
795             case VER_GREATER_EQUAL:
796                 if(ver.dwMajorVersion < lpVersionInfo->dwMajorVersion)
797                     res = FALSE;
798                 break;
799             case VER_LESS:
800                 if(ver.dwMajorVersion >= lpVersionInfo->dwMajorVersion)
801                     res = FALSE;
802                 break;
803             case VER_LESS_EQUAL:
804                 if(ver.dwMajorVersion > lpVersionInfo->dwMajorVersion)
805                     res = FALSE;
806                 break;
807             default:
808                 res = FALSE;
809                 SetLastError(ERROR_BAD_ARGUMENTS);
810                 error_set = TRUE;
811         }
812     if(dwTypeMask & VER_MINORVERSION && res)
813         switch(dwlConditionMask >> 0*3 & 0x07) {
814             case VER_EQUAL:
815                 if(ver.dwMinorVersion != lpVersionInfo->dwMinorVersion)
816                     res = FALSE;
817                 break;
818             case VER_GREATER:
819                 if(ver.dwMinorVersion <= lpVersionInfo->dwMinorVersion)
820                     res = FALSE;
821                 break;
822             case VER_GREATER_EQUAL:
823                 if(ver.dwMinorVersion < lpVersionInfo->dwMinorVersion)
824                     res = FALSE;
825                 break;
826             case VER_LESS:
827                 if(ver.dwMinorVersion >= lpVersionInfo->dwMinorVersion)
828                     res = FALSE;
829                 break;
830             case VER_LESS_EQUAL:
831                 if(ver.dwMinorVersion > lpVersionInfo->dwMinorVersion)
832                     res = FALSE;
833                 break;
834             default:
835                 res = FALSE;
836                 SetLastError(ERROR_BAD_ARGUMENTS);
837                 error_set = TRUE;
838         }
839     if(dwTypeMask & VER_SERVICEPACKMAJOR && res)
840         switch(dwlConditionMask >> 5*3 & 0x07) {
841             case VER_EQUAL:
842                 if(ver.wServicePackMajor != lpVersionInfo->wServicePackMajor)
843                     res = FALSE;
844                 break;
845             case VER_GREATER:
846                 if(ver.wServicePackMajor <= lpVersionInfo->wServicePackMajor)
847                     res = FALSE;
848                 break;
849             case VER_GREATER_EQUAL:
850                 if(ver.wServicePackMajor < lpVersionInfo->wServicePackMajor)
851                     res = FALSE;
852                 break;
853             case VER_LESS:
854                 if(ver.wServicePackMajor >= lpVersionInfo->wServicePackMajor)
855                     res = FALSE;
856                 break;
857             case VER_LESS_EQUAL:
858                 if(ver.wServicePackMajor > lpVersionInfo->wServicePackMajor)
859                     res = FALSE;
860                 break;
861             default:
862                 res = FALSE;
863                 SetLastError(ERROR_BAD_ARGUMENTS);
864                 error_set = TRUE;
865         }
866     if(dwTypeMask & VER_SERVICEPACKMINOR && res)
867         switch(dwlConditionMask >> 4*3 & 0x07) {
868             case VER_EQUAL:
869                 if(ver.wServicePackMinor != lpVersionInfo->wServicePackMinor)
870                     res = FALSE;
871                 break;
872             case VER_GREATER:
873                 if(ver.wServicePackMinor <= lpVersionInfo->wServicePackMinor)
874                     res = FALSE;
875                 break;
876             case VER_GREATER_EQUAL:
877                 if(ver.wServicePackMinor < lpVersionInfo->wServicePackMinor)
878                     res = FALSE;
879                 break;
880             case VER_LESS:
881                 if(ver.wServicePackMinor >= lpVersionInfo->wServicePackMinor)
882                     res = FALSE;
883                 break;
884             case VER_LESS_EQUAL:
885                 if(ver.wServicePackMinor > lpVersionInfo->wServicePackMinor)
886                     res = FALSE;
887                 break;
888             default:
889                 res = FALSE;
890                 SetLastError(ERROR_BAD_ARGUMENTS);
891                 error_set = TRUE;
892         }
893
894     if(!(res || error_set))
895         SetLastError(ERROR_OLD_WIN_VERSION);
896     return res;
897 }
898
899
900 /***********************************************************************
901  *          GetWinFlags   (KERNEL.132)
902  */
903 DWORD WINAPI GetWinFlags16(void)
904 {
905   static const long cpuflags[5] =
906     { WF_CPU086, WF_CPU186, WF_CPU286, WF_CPU386, WF_CPU486 };
907   SYSTEM_INFO si;
908   OSVERSIONINFOA ovi;
909   DWORD result;
910
911   GetSystemInfo(&si);
912
913   /* There doesn't seem to be any Pentium flag.  */
914   result = cpuflags[min(si.wProcessorLevel, 4)] | WF_ENHANCED | WF_PMODE | WF_80x87 | WF_PAGING;
915   if (si.wProcessorLevel >= 4) result |= WF_HASCPUID;
916   ovi.dwOSVersionInfoSize = sizeof(ovi);
917   GetVersionExA(&ovi);
918   if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
919       result |= WF_WIN32WOW; /* undocumented WF_WINNT */
920   return result;
921 }
922
923
924 #if 0
925 /* Not used at this time. This is here for documentation only */
926
927 /* WINDEBUGINFO flags values */
928 #define WDI_OPTIONS         0x0001
929 #define WDI_FILTER          0x0002
930 #define WDI_ALLOCBREAK      0x0004
931
932 /* dwOptions values */
933 #define DBO_CHECKHEAP       0x0001
934 #define DBO_BUFFERFILL      0x0004
935 #define DBO_DISABLEGPTRAPPING 0x0010
936 #define DBO_CHECKFREE       0x0020
937
938 #define DBO_SILENT          0x8000
939
940 #define DBO_TRACEBREAK      0x2000
941 #define DBO_WARNINGBREAK    0x1000
942 #define DBO_NOERRORBREAK    0x0800
943 #define DBO_NOFATALBREAK    0x0400
944 #define DBO_INT3BREAK       0x0100
945
946 /* DebugOutput flags values */
947 #define DBF_TRACE           0x0000
948 #define DBF_WARNING         0x4000
949 #define DBF_ERROR           0x8000
950 #define DBF_FATAL           0xc000
951
952 /* dwFilter values */
953 #define DBF_KERNEL          0x1000
954 #define DBF_KRN_MEMMAN      0x0001
955 #define DBF_KRN_LOADMODULE  0x0002
956 #define DBF_KRN_SEGMENTLOAD 0x0004
957 #define DBF_USER            0x0800
958 #define DBF_GDI             0x0400
959 #define DBF_MMSYSTEM        0x0040
960 #define DBF_PENWIN          0x0020
961 #define DBF_APPLICATION     0x0008
962 #define DBF_DRIVER          0x0010
963
964 #endif /* NOLOGERROR */
965
966
967 /***********************************************************************
968  *          GetWinDebugInfo   (KERNEL.355)
969  */
970 BOOL16 WINAPI GetWinDebugInfo16(WINDEBUGINFO16 *lpwdi, UINT16 flags)
971 {
972     FIXME("(%8lx,%d): stub returning 0\n",
973           (unsigned long)lpwdi, flags);
974     /* 0 means not in debugging mode/version */
975     /* Can this type of debugging be used in wine ? */
976     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
977     return 0;
978 }
979
980
981 /***********************************************************************
982  *          SetWinDebugInfo   (KERNEL.356)
983  */
984 BOOL16 WINAPI SetWinDebugInfo16(WINDEBUGINFO16 *lpwdi)
985 {
986     FIXME("(%8lx): stub returning 0\n", (unsigned long)lpwdi);
987     /* 0 means not in debugging mode/version */
988     /* Can this type of debugging be used in wine ? */
989     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
990     return 0;
991 }
992
993
994 /***********************************************************************
995  *           K329                    (KERNEL.329)
996  *
997  * TODO:
998  * Should fill lpBuffer only if DBO_BUFFERFILL has been set by SetWinDebugInfo()
999  */
1000 void WINAPI DebugFillBuffer(LPSTR lpBuffer, WORD wBytes)
1001 {
1002         memset(lpBuffer, DBGFILL_BUFFER, wBytes);
1003 }
1004
1005 /***********************************************************************
1006  *           DiagQuery                          (KERNEL.339)
1007  *
1008  * returns TRUE if Win called with "/b" (bootlog.txt)
1009  */
1010 BOOL16 WINAPI DiagQuery16(void)
1011 {
1012         /* perhaps implement a Wine "/b" command line flag sometime ? */
1013         return FALSE;
1014 }
1015
1016 /***********************************************************************
1017  *           DiagOutput                         (KERNEL.340)
1018  *
1019  * writes a debug string into <windir>\bootlog.txt
1020  */
1021 void WINAPI DiagOutput16(LPCSTR str)
1022 {
1023         /* FIXME */
1024         DPRINTF("DIAGOUTPUT:%s\n", debugstr_a(str));
1025 }