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